[
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL\"\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [ main ]\n  schedule:\n    - cron: '25 19 * * 2'\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ 'go' ]\n        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]\n        # Learn more about CodeQL language support at https://git.io/codeql-language-support\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v2\n\n    # Initializes the CodeQL tools for scanning.\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v1\n      with:\n        languages: ${{ matrix.language }}\n        # If you wish to specify custom queries, you can do so here or in a config file.\n        # By default, queries listed here will override any specified in a config file.\n        # Prefix the list here with \"+\" to use these queries and those in the config file.\n        # queries: ./path/to/local/query, your-org/your-repo/queries@main\n\n    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).\n    # If this step fails, then you should remove it and run the build manually (see below)\n    - name: Autobuild\n      uses: github/codeql-action/autobuild@v1\n\n    # ℹ️ Command-line programs to run using the OS shell.\n    # 📚 https://git.io/JvXDl\n\n    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines\n    #    and modify them (or add more) to build your code if your project\n    #    uses a compiled language\n\n    #- run: |\n    #   make bootstrap\n    #   make release\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v1\n"
  },
  {
    "path": ".github/workflows/docker.yml",
    "content": "name: Build Docker\non:\n  push:\n    tags:\n      - 'v*.*.*'\n\njobs:\n  docker:\n    name: Build docker\n    runs-on: ubuntu-latest\n    steps:\n      - name: Get Release Version\n        run: |\n          export RELEASE_VERSION=${GITHUB_REF#refs/*/}\n          echo RELEASE_VERSION: ${RELEASE_VERSION}\n          echo \"RELEASE_VERSION=${RELEASE_VERSION}\" >> $GITHUB_ENV\n\n      - name: Check out code\n        uses: actions/checkout@v3\n\n      - name: Create Docker\n        id: meta\n        uses: docker/metadata-action@v3\n        with:\n          images: |\n            yedf/dtm\n          tags: |\n            type=semver,pattern={{version}}\n            type=semver,pattern={{major}}.{{minor}}\n            type=semver,pattern={{major}}\n            type=sha\n\n      - name: Set up QEMU\n        uses: docker/setup-qemu-action@v1\n\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v1\n\n      - name: Login to DockerHub\n        uses: docker/login-action@v1\n        with:\n          username: ${{ secrets.DOCKERHUB_USERNAME }}\n          password: ${{ secrets.DOCKERHUB_TOKEN }}\n\n      - name: Build and push\n        uses: docker/build-push-action@v2\n        with:\n          context: .\n          file: ./helper/Dockerfile-release\n          push: true\n          platforms: linux/amd64,linux/arm64\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n          build-args: |\n            RELEASE_VERSION=${{ env.RELEASE_VERSION }}\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\non:\n  push:\n    tags:\n      - 'v*.*.*'\n\njobs:\n  release:\n    name: Release on GitHub\n    runs-on: ubuntu-latest\n    steps:\n      - name: Get Release Version\n        run: |\n          export RELEASE_VERSION=${GITHUB_REF#refs/*/}\n          echo RELEASE_VERSION: ${RELEASE_VERSION}\n          echo \"RELEASE_VERSION=${RELEASE_VERSION}\" >> $GITHUB_ENV\n\n      - name: Check out code\n        uses: actions/checkout@v3\n\n      - name: Validates GO releaser config\n        uses: docker://goreleaser/goreleaser:v1.7.0\n        with:\n          args: check\n\n      - name: Create release on GitHub\n        uses: docker://goreleaser/goreleaser:v1.7.0\n        with:\n          args: release -f helper/.goreleaser.yml --rm-dist\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n\n      - name: Setup node\n        uses: actions/setup-node@v3\n        with:\n          node-version: 14\n\n      - name: Build admin\n        run: |\n          cd admin\n          npm install -g yarn\n          yarn\n          VITE_ADMIN_VERSION=${{ env.RELEASE_VERSION }} yarn build\n          cd ..\n\n      - name: Scp admin\n        env:\n          host: 'ubuntu@en.dtm.pub'\n          host2: 'ubuntu@dtm.pub'\n          dest: '/data/dtm-admin/'\n        run: |\n          cd admin\n          echo \"${{secrets.DEPLOY_KEY}}\" > deploy_key\n          chmod 600 ./deploy_key\n          tar -cvzf dist.tar.gz dist\n          scp -i deploy_key -o StrictHostKeyChecking=no dist.tar.gz ${{env.host}}:${{env.dest}}\n          ssh -i deploy_key -o StrictHostKeyChecking=no ${{env.host}} 'cd ${{env.dest}} && tar -zvxf dist.tar.gz'\n          scp -i deploy_key -o StrictHostKeyChecking=no dist.tar.gz ${{env.host2}}:${{env.dest}}\n          ssh -i deploy_key -o StrictHostKeyChecking=no ${{env.host2}} 'cd ${{env.dest}} && tar -zvxf dist.tar.gz'\n          rm deploy_key dist.tar.gz\n          echo > dist/placeholder\n          cd ..\n"
  },
  {
    "path": ".github/workflows/tests.yml",
    "content": "name: Tests\non:\n  push:\n    branches-ignore:\n      - 'tmp-*'\n  pull_request:\n    branches-ignore:\n      - 'tmp-*'\n\njobs:\n  tests:\n    name: CI\n    runs-on: ubuntu-latest\n    services:\n      mysql:\n        image: 'mysql:8'\n        env:\n          MYSQL_ALLOW_EMPTY_PASSWORD: 1\n        volumes:\n          - /etc/localtime:/etc/localtime:ro\n          - /etc/timezone:/etc/timezone:ro\n        ports:\n          - 3306:3306\n      redis:\n        image: 'redis'\n        volumes:\n          - /etc/localtime:/etc/localtime:ro\n          - /etc/timezone:/etc/timezone:ro\n        ports:\n          - 6379:6379\n      postgres:\n        image: 'yedf/postgres-xa'\n        volumes:\n          - /etc/localtime:/etc/localtime:ro\n          - /etc/timezone:/etc/timezone:ro\n        env:\n          POSTGRES_PASSWORD: mysecretpassword\n          POSTGRES_DB: dtm\n        ports:\n          - '5432:5432'\n      mongo:\n        image: 'yedf/mongo-rs'\n        volumes:\n          - /etc/localtime:/etc/localtime:ro\n          - /etc/timezone:/etc/timezone:ro\n        ports:\n          - 27017:27017\n      sqlserver:\n        image: mcr.microsoft.com/mssql/server:2019-latest\n        volumes:\n          - /etc/localtime:/etc/localtime:ro\n          - /etc/timezone:/etc/timezone:ro\n        env:\n          ACCEPT_EULA: Y\n          MSSQL_SA_PASSWORD: p@ssw0rd\n        ports:\n          - '1433:1433'\n    steps:\n      - name: Set up Go 1.23\n        uses: actions/setup-go@v2\n        with:\n          go-version: '1.23.3'\n\n      - name: Check out code\n        uses: actions/checkout@v2\n\n      - name: Install dependencies\n        run: |\n          go mod download\n\n      - name: Run CI lint\n        run: sh helper/golint.sh\n\n      - name: Run test cover\n        run: sh helper/test-cover.sh\n      - name: Upload coverage to Codecov\n        run: bash <(curl -s https://codecov.io/bash)\n        env:\n          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}"
  },
  {
    "path": ".gitignore",
    "content": "conf.yml\n*.out\n*.log\n# dist\n.idea/**\n.vscode\ndefault.etcd\n*/**/*.bolt\nbench/bench\nhelper/bench/bench\nhelper/qs/qs\n# Output file of unit test coverage\ncoverage.*\nprofile.*\ntest.sh\ndtm\ndtm-*\ndtm.*\ncache\n\n"
  },
  {
    "path": "LICENSE",
    "content": "BSD 3-Clause License\n\nCopyright (c) 2021, yedf\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its\n   contributors may be used to endorse or promote products derived from\n   this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "Makefile",
    "content": "# dev env https://www.dtm.pub/other/develop.html\nall: fmt lint test_redis\n.PHONY: all\n\nfmt:\n\t@gofmt -s -w ./\n\nlint:\n\trevive -config revive.toml ./...\n\n.PHONY: test\ntest:\n\t@go test ./...\n\ntest_redis:\n\tTEST_STORE=redis go test ./...\n\ntest_all:\n\tTEST_STORE=redis go test ./...\n\tTEST_STORE=boltdb go test ./...\n\tTEST_STORE=mysql go test ./...\n\tTEST_STORE=postgres go test ./...\n\ncover_test:\n\t./helper/test-cover.sh\n\n"
  },
  {
    "path": "README.md",
    "content": "![license](https://img.shields.io/github/license/dtm-labs/dtm)\n![Build Status](https://github.com/dtm-labs/dtm/actions/workflows/tests.yml/badge.svg?branch=main)\n[![codecov](https://codecov.io/gh/dtm-labs/dtm/branch/main/graph/badge.svg?token=UKKEYQLP3F)](https://codecov.io/gh/dtm-labs/dtm)\n[![Go Report Card](https://goreportcard.com/badge/github.com/dtm-labs/dtm)](https://goreportcard.com/report/github.com/dtm-labs/dtm)\n[![Go Reference](https://pkg.go.dev/badge/github.com/dtm-labs/dtm.svg)](https://pkg.go.dev/github.com/dtm-labs/dtm)\n[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#database)\n\nEnglish | [简体中文](https://github.com/dtm-labs/dtm/blob/main/helper/README-cn.md)\n\n# Distributed Transactions Manager\n\n## What is DTM\n\nDTM is a distributed transaction framework which provides cross-service eventual data consistency. It provides saga, tcc, xa, 2-phase message, outbox, workflow patterns for a variety of application scenarios. It also supports multiple languages and multiple store engine to form up a transaction as following:\n\n<img alt=\"function-picture\" src=\"https://en.dtm.pub/assets/function.7d5618f8.png\" height=250 />\n\n## Who's using DTM (partial)\n\n[Tencent](https://en.dtm.pub/other/using.html#tencent)\n\n[Bytedance](https://en.dtm.pub/other/using.html#bytedance)\n\n[Ivydad](https://en.dtm.pub/other/using.html#ivydad)\n\n[More](https://en.dtm.pub/other/using.html)\n\n## Features\n* Support for multiple transaction modes: SAGA, TCC, XA, Workflow, Outbox\n* Multiple languages support: SDK for Go, Java, PHP, C#, Python, Nodejs\n* Better Outbox: 2-phase messages, a more elegant solution than Outbox, support multi-databases\n* Multiple database transaction support: MySQL/MariaDB, Redis, MongoDB, Postgres, TDSQL, etc.\n* Support for multiple storage engines: MySQL/MariaDB (common), Redis (high performance), BoltDB (dev&test), MongoDB (under planning)\n* Support for multiple microservices architectures: [go-zero](https://github.com/zeromicro/go-zero), go-kratos/kratos, polarismesh/polaris\n* Support for high availability and easy horizontal scaling\n\n## Application scenarios.\nDTM can be applied to data consistency issues in a large number of scenarios, here are a few common ones\n* [cache management](https://en.dtm.pub/app/cache.html): thoroughly guarantee the cache final consistency and strong consistency\n* [flash-sales to deduct inventory](https://en.dtm.pub/app/flash.html): in extreme cases, it is also possible to ensure that the precise inventory in Redis is exactly the same as the final order created, without the need for manual adjustment\n* [Non-monolithic order system](https://en.dtm.pub/app/order.html): Dramatically simplifies the architecture\n* [Event publishing/subscription](https://en.dtm.pub/practice/msg.html): better outbox pattern\n\n## [Cook Book](https://en.dtm.pub)\n\n## Quick start\n\n### run dtm\n\n``` bash\ngit clone https://github.com/dtm-labs/dtm && cd dtm\ngo run main.go\n```\n\n### Start an example\nSuppose we want to perform an inter-bank transfer. The operations of transfer out (TransOut) and transfer in (TransIn) are coded in separate micro-services.\n\nHere is an example to illustrate a solution of dtm to this problem:\n\n``` bash\ngit clone https://github.com/dtm-labs/quick-start-sample.git && cd quick-start-sample/workflow-grpc\ngo run main.go\n```\n\n## Code\n\n### Usage\n``` go\nwfName := \"workflow-grpc\"\nerr = workflow.Register(wfName, func(wf *workflow.Workflow, data []byte) error {\n  // ...\n  // Define a transaction branch for TransOut\n  wf.NewBranch().OnRollback(func(bb *dtmcli.BranchBarrier) error {\n    // compensation for TransOut\n    _, err := busiCli.TransOutRevert(wf.Context, &req)\n    return err\n  })\n  _, err = busiCli.TransOut(wf.Context, &req)\n  // check error\n\n  // Define another transaction branch for TransIn\n  wf.NewBranch().OnRollback(func(bb *dtmcli.BranchBarrier) error {\n    _, err := busiCli.TransInRevert(wf.Context, &req)\n    return err\n  })\n  _, err = busiCli.TransIn(wf.Context, &req)\n  return err\n}\n\n// ...\nreq := busi.BusiReq{Amount: 30, TransInResult: \"\"}\ndata, err := proto.Marshal(&req)\n\n// Execute workflow\n_, err = workflow.ExecuteCtx(wfName, shortuuid.New(), data)\nlogger.Infof(\"result of workflow.Execute is: %v\", err)\n\n```\n\nWhen the above code runs, we can see in the console that services `TransOut`, `TransIn` has been called.\n\n#### Rollback upon failure\nIf any forward operation fails, DTM invokes the corresponding compensating operation of each sub-transaction to roll back, after which the  transaction is successfully rolled back.\n\nLet's purposely trigger the failure of the second sub-transaction and watch what happens\n\n``` go\n// req := busi.BusiReq{Amount: 30, TransInResult: \"\"}\nreq := busi.BusiReq{Amount: 30, TransInResult: \"FAILURE\"}\n})\n```\n\nwe can see in the console that services `TransOut`, `TransIn`, `TransOutRevert` has been called\n\n## More examples\nIf you want more quick start examples, please refer to [dtm-labs/quick-start-sample](https://github.com/dtm-labs/quick-start-sample)\n\nThe above example mainly demonstrates the flow of a distributed transaction. More on this, including practical examples of how to interact with an actual database, how to do compensation, how to do rollback, etc. please refer to [dtm-examples](https://github.com/dtm-labs/dtm-examples) for more examples.\n\n## Give a star! ⭐\n\nIf you think this project is interesting, or helpful to you, please give a star!\n"
  },
  {
    "path": "admin/.eslintrc.js",
    "content": "module.exports = {\n    parser: 'vue-eslint-parser',\n    env: {\n        browser: true,\n        es2021: true\n    },\n    extends: [\n        'plugin:@typescript-eslint/recommended',\n        'plugin:vue/vue3-recommended'\n    ],\n    parserOptions: {\n        parser: '@typescript-eslint/parser',\n        sourceType: 'module',\n        ecmaFeature: {\n            jsx: true,\n            tsx: true\n        }\n    },\n    plugins: [\n        '@typescript-eslint'\n    ],\n    rules: {\n        'vue/max-attributes-per-line': ['error', {\n            singleline: 10,\n            multiline: {\n                max: 1\n            }\n        }],\n        'vue/multi-word-component-names': 0,\n        'vue/singleline-html-element-content-newline': 'off',\n        'vue/multiline-html-element-content-newline': 'off',\n        'vue/html-indent': ['error', 4],\n        indent: ['error', 4], // 4行缩进\n        'vue/script-indent': ['error', 4],\n        quotes: ['error', 'single'], // 单引号\n        // 'vue/html-quotes': ['error', 'single'],\n        semi: ['error', 'never'], // 禁止使用分号\n        'space-infix-ops': ['error', {\n            int32Hint: false\n        }], // 要求操作符周围有空格\n        'no-multi-spaces': 'error', // 禁止多个空格\n        'no-whitespace-before-property': 'error', // 禁止在属性前使用空格\n        'space-before-blocks': 'error', // 在块之前强制保持一致的间距\n        'space-before-function-paren': ['error', 'never'], // 在“ function”定义打开括号之前强制不加空格\n        'space-in-parens': ['error', 'never'], // 强制括号左右的不加空格\n        'spaced-comment': ['error', 'always'], // 注释间隔\n        'template-tag-spacing': ['error', 'always'], // 在模板标签及其文字之间需要空格\n        'no-var': 'error',\n        'prefer-destructuring': ['error', { // 优先使用数组和对象解构\n            array: true,\n            object: true\n        }, {\n            enforceForRenamedProperties: false\n        }],\n        'comma-dangle': ['error', 'never'], // 最后一个属性不允许有逗号\n        'arrow-spacing': 'error', // 箭头函数空格\n        'prefer-template': 'error',\n        'template-curly-spacing': 'error',\n        'quote-props': ['error', 'as-needed'], // 对象字面量属性名称使用引号\n        'object-curly-spacing': ['error', 'always'], // 强制在花括号中使用一致的空格\n        'no-unneeded-ternary': 'error', // 禁止可以表达为更简单结构的三元操作符\n        'no-restricted-syntax': ['error', 'WithStatement', 'BinaryExpression[operator=\"in\"]'], // 禁止with/in语句\n        'no-lonely-if': 'error', // 禁止 if 语句作为唯一语句出现在 else 语句块中\n        'newline-per-chained-call': ['error', {\n            ignoreChainWithDepth: 2\n        }], // 要求方法链中每个调用都有一个换行符\n        // 路径别名设置\n        'no-submodule-imports': ['off', '/@'],\n        'no-implicit-dependencies': ['off', ['/@']],\n        '@typescript-eslint/no-explicit-any': 'off' // 类型可以使用any \n    }\n}"
  },
  {
    "path": "admin/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "admin/README.md",
    "content": "# DTM-Admin\n"
  },
  {
    "path": "admin/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" href=\"<%= PUBLIC_PATH %>/favicon.ico\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Dtm</title>\n    <script type=\"text/javascript\">\n        window.basePath = '<%= PUBLIC_PATH %>';\n\n        window.__assetsPathBuilder = function (importer) {\n            return window.basePath + \"/\" + importer;\n        };\n    </script>\n</head>\n\n<body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "admin/package.json",
    "content": "{\n  \"name\": \"dtm-admin\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build && echo > dist/placeholder\",\n    \"preview\": \"vite preview\",\n    \"lint\": \"eslint --ext .tsx,.ts,vue src/\",\n    \"lint:fix\": \"eslint --ext .tsx,.ts,vue src/ --fix\"\n  },\n  \"dependencies\": {\n    \"ant-design-vue\": \"^3.1.1\",\n    \"vue\": \"^3.2.25\",\n    \"vue-demi\": \"^0.13.11\",\n    \"vue-request\": \"^1.2.4\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^17.0.23\",\n    \"@types/nprogress\": \"^0.2.0\",\n    \"@typescript-eslint/eslint-plugin\": \"^5.18.0\",\n    \"@typescript-eslint/parser\": \"^5.18.0\",\n    \"@vitejs/plugin-vue\": \"^4.0.0\",\n    \"autoprefixer\": \"^10.4.4\",\n    \"axios\": \"^1.7.4\",\n    \"eslint\": \"^8.33.0\",\n    \"eslint-plugin-vue\": \"^8.6.0\",\n    \"fast-glob\": \"^3.2.11\",\n    \"nprogress\": \"^0.2.0\",\n    \"pinia\": \"^2.0.0-rc.10\",\n    \"postcss\": \"^8.4.31\",\n    \"postcss-import\": \"^14.1.0\",\n    \"postcss-nested\": \"^5.0.6\",\n    \"postcss-simple-vars\": \"^6.0.3\",\n    \"prettier\": \"^2.8.3\",\n    \"prettier-eslint\": \"^15.0.1\",\n    \"screenfull\": \"^6.0.1\",\n    \"tailwindcss\": \"^3.0.24\",\n    \"typescript\": \"^4.5.4\",\n    \"unplugin-vue-components\": \"^0.25.1\",\n    \"vite\": \"^3.2.11\",\n    \"vite-plugin-ejs\": \"^1.6.4\",\n    \"vite-plugin-svg-icons\": \"^2.0.1\",\n    \"vue-router\": \"^4.0.13\",\n    \"vue-tsc\": \"^0.29.8\"\n  }\n}\n"
  },
  {
    "path": "admin/postcss.config.js",
    "content": "module.exports = {\n    plugins: {\n        tailwindcss: {},\n        autoprefixer: {}\n    }\n}"
  },
  {
    "path": "admin/src/App.vue",
    "content": "<template>\n    <router-view />\n</template>\n\n<script setup lang=\"ts\"></script>\n\n"
  },
  {
    "path": "admin/src/api/api_dtm.ts",
    "content": "import { AxiosResponse } from 'axios'\nimport request from '/@/utils/request'\n\nexport interface IListAllTransactionsReq {\n    gid?: string;\n    limit: number;\n    position?: string;\n}\n\nexport interface IListAllKVReq {\n    cat: string;\n    limit: number;\n    position?: string;\n}\n\nexport function listAllTransactions<T>(\n    payload: IListAllTransactionsReq\n): Promise<AxiosResponse<T>> {\n    return request({\n        url: '/api/dtmsvr/all',\n        method: 'get',\n        params: payload\n    })\n}\n\nexport function forceStopTransaction(gid: string): Promise<AxiosResponse> {\n    return request({\n        url: '/api/dtmsvr/forceStop',\n        method: 'post',\n        data: { gid }\n    })\n}\n\nexport function queryKVPair<T>(payload: {\n    cat: string;\n    key: string;\n}): Promise<AxiosResponse<T>> {\n    return request({\n        url: '/api/dtmsvr/queryKV',\n        method: 'get',\n        params: payload\n    })\n}\n\nexport function listKVPairs<T>(\n    payload: IListAllKVReq\n): Promise<AxiosResponse<T>> {\n    return request({\n        url: '/api/dtmsvr/scanKV',\n        method: 'get',\n        params: payload\n    })\n}\n\nexport function deleteTopic<T>(topicName: string): Promise<AxiosResponse<T>> {\n    return request({\n        url: `/api/dtmsvr/topic/${topicName}`,\n        method: 'delete'\n    })\n}\n\nexport function subscribe<T>(payload: {\n    topic: string;\n    url: string;\n    remark: string;\n}): Promise<AxiosResponse<T>> {\n    return request({\n        url: '/api/dtmsvr/subscribe',\n        method: 'get',\n        params: payload\n    })\n}\n\nexport function unsubscribe(payload: {\n    topic: string;\n    url: string;\n}): Promise<AxiosResponse> {\n    return request({\n        url: '/api/dtmsvr/unsubscribe',\n        method: 'get',\n        params: payload\n    })\n}\n\nexport function getTransaction<T>(payload: {\n    gid: string;\n}): Promise<AxiosResponse<T>> {\n    return request({\n        url: '/api/dtmsvr/query',\n        method: 'get',\n        params: payload\n    })\n}\n\nexport function resetNextCronTime(gid: string): Promise<AxiosResponse> {\n    return request({\n        url: '/api/dtmsvr/resetNextCronTime',\n        method: 'post',\n        data: { gid }\n    })\n}\n\nexport function getDtmVersion(): Promise<AxiosResponse<any>> {\n    return request({\n        url: '/api/dtmsvr/version',\n        method: 'get'\n    })\n}\n"
  },
  {
    "path": "admin/src/assets/css/index.css",
    "content": "/* @tailwind base; */\n@tailwind components;\n@tailwind utilities;\n"
  },
  {
    "path": "admin/src/components/Screenfull/index.vue",
    "content": "<template>\n    <div class=\"hidden-xs-only px-2\">\n        <svg-icon v-if=\"!isFulScreen\" class-name=\"cursor-pointer\" icon-class=\"svg-fullscreen\" @click=\"changeScreenfull(identity)\" />\n        <svg-icon v-else class-name=\"cursor-pointer\" icon-class=\"svg-exit-fullscreen\" @click=\"changeScreenfull(identity)\" />\n    </div>\n</template>\n<script setup lang='ts'>\nimport { notification } from 'ant-design-vue'\nimport { onMounted, onUnmounted, ref } from 'vue'\nimport screenfull from 'screenfull'\n\nconst isFulScreen = ref(false)\nconst changeScreenfull = (identity: string) => {\n    if (!screenfull.isEnabled) {\n        notification.open({\n            message: '浏览器不支持全屏',\n            type: 'warning'\n        })\n    } else if (identity) {\n        const element = document.getElementById(identity)\n        screenfull.toggle(element as HTMLElement)\n    } else {\n        screenfull.toggle()\n    }\n}\nconst change = () => {\n    if (screenfull.isEnabled) isFulScreen.value = screenfull.isFullscreen\n}\n\ndefineProps({\n    identity: {\n        type: String,\n        default: null\n    }\n})\n\nconst emits = defineEmits(['screen'])\nonMounted(() => screenfull.isEnabled && screenfull.on('change', change) && emits('screen'))\nonUnmounted(() => screenfull.isEnabled && screenfull.off('change', change))\n</script>\n"
  },
  {
    "path": "admin/src/components/SvgIcon/index.vue",
    "content": "<template>\n    <div v-if=\"isExternal\" :style=\"styleExternalIcon\" class=\"svg-external-icon svg-icon\" />\n    <svg v-else :class=\"svgClass\" aria-hidden=\"true\">\n        <use :xlink:href=\"iconName\" />\n    </svg>\n</template>\n<script setup lang='ts'>\n\nconst props = defineProps({\n    iconClass: {\n        type: String,\n        required: true\n    },\n    className: {\n        type: String,\n        default: ''\n    }\n})\n\nconst isExternal = /^(https?:|mailto:|tel:)/.test(props.iconClass)\nconst iconName = `#icon-${props.iconClass}`\nconst svgClass = props.className ? `svg-icon ${props.className}` : 'svg-icon '\nconst styleExternalIcon = () => {\n    return {\n        mask: `url(${props.iconClass}) no-repeat 50% 50%`,\n        '-webkit-mask': `url(${props.iconClass}) no-repeat 50% 50%`\n    }\n}\n</script>\n\n<style lang=\"postcss\" scoped>\n.svg-icon {\n    width: 1em;\n    height: 1em;\n    vertical-align: -0.15em;\n    fill: currentColor;\n    overflow: hidden;\n}\n.svg-external-icon {\n    background-color: currentColor;\n    mask-size: cover !important;\n    display: inline-block;\n}\n</style>\n"
  },
  {
    "path": "admin/src/components.d.ts",
    "content": "/* eslint-disable */\n/* prettier-ignore */\n// @ts-nocheck\n// Generated by unplugin-vue-components\n// Read more: https://github.com/vuejs/core/pull/3399\nexport {}\n\ndeclare module 'vue' {\n  export interface GlobalComponents {\n    AAlert: typeof import('ant-design-vue/es')['Alert']\n    ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb']\n    ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem']\n    AButton: typeof import('ant-design-vue/es')['Button']\n    ADescriptions: typeof import('ant-design-vue/es')['Descriptions']\n    ADescriptionsItem: typeof import('ant-design-vue/es')['DescriptionsItem']\n    ADivider: typeof import('ant-design-vue/es')['Divider']\n    AForm: typeof import('ant-design-vue/es')['Form']\n    AFormItem: typeof import('ant-design-vue/es')['FormItem']\n    AInput: typeof import('ant-design-vue/es')['Input']\n    ALayout: typeof import('ant-design-vue/es')['Layout']\n    ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent']\n    ALayoutHeader: typeof import('ant-design-vue/es')['LayoutHeader']\n    ALayoutSider: typeof import('ant-design-vue/es')['LayoutSider']\n    AMenu: typeof import('ant-design-vue/es')['Menu']\n    AMenuItem: typeof import('ant-design-vue/es')['MenuItem']\n    AModal: typeof import('ant-design-vue/es')['Modal']\n    APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']\n    ARangePicker: typeof import('ant-design-vue/es')['RangePicker']\n    ASelect: typeof import('ant-design-vue/es')['Select']\n    ASelectOption: typeof import('ant-design-vue/es')['SelectOption']\n    ASubMenu: typeof import('ant-design-vue/es')['SubMenu']\n    ATable: typeof import('ant-design-vue/es')['Table']\n    ATag: typeof import('ant-design-vue/es')['Tag']\n    ATextarea: typeof import('ant-design-vue/es')['Textarea']\n    RouterLink: typeof import('vue-router')['RouterLink']\n    RouterView: typeof import('vue-router')['RouterView']\n    Screenfull: typeof import('./components/Screenfull/index.vue')['default']\n    SvgIcon: typeof import('./components/SvgIcon/index.vue')['default']\n  }\n}\n"
  },
  {
    "path": "admin/src/icons/readme.md",
    "content": "# ICon Component\n"
  },
  {
    "path": "admin/src/layout/aside.vue",
    "content": "<template>\n    <a-layout>\n        <a-layout-sider width=\"200\" style=\"background: #fff\">\n            <Sidebar />\n        </a-layout-sider>\n        <a-layout style=\"padding: 0 24px 24px\">\n            <div v-if=\"layout.dtmVersion && layout.dtmVersion != dashVer\" style=\"color:#f00\"> !!! admin version: {{ dashVer }} != dtm version: {{ layout.dtmVersion }}. </div>\n            <a-breadcrumb style=\"margin: 16px 0\">\n                <a-breadcrumb-item>{{ mainNav }}</a-breadcrumb-item>\n                <a-breadcrumb-item>{{ subNav }}</a-breadcrumb-item>\n                <a-breadcrumb-item>{{ page }}</a-breadcrumb-item>\n            </a-breadcrumb>\n            <a-layout-content\n                :style=\"{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px' }\"\n            >\n                <Content />\n            </a-layout-content>\n        </a-layout>\n    </a-layout>\n</template>\n\n<script setup lang='ts'>\nimport Sidebar from './components/sidebar.vue'\nimport Content from './components/content.vue'\nimport { useRoute } from 'vue-router'\nimport { useLayoutStore } from '../store/modules/layout'\nimport { findCurrentMenubar } from '../utils/util'\nimport { computed, onMounted } from 'vue'\n\n\nconst dashVer = import.meta.env.VITE_ADMIN_VERSION\nconst route = useRoute()\nconst layout = useLayoutStore()\n\nconst mainNav = computed(() => {\n    const currentMenubar = findCurrentMenubar(layout.getMenubar.menuList, true)\n    return currentMenubar?.meta.title\n})\n\nconst subNav = computed(() => {\n    let subNav = ''\n    const currentMenubar = findCurrentMenubar(layout.getMenubar.menuList, true)\n    currentMenubar.children?.forEach(v => {\n        if (route.path.indexOf(v.path) !== -1) {\n            subNav = v.meta.title\n        }\n    })\n\n    return subNav\n})\n\nconst page = computed(() => {\n    let page = ''\n    const currentMenubar = findCurrentMenubar(layout.getMenubar.menuList, true)\n    currentMenubar.children?.forEach(v => {\n        v.children?.forEach(vv => {\n            if (route.path == vv.path) {\n                page = vv.meta.title\n            }\n        })\n    })\n\n    return page\n})\n\nonMounted(() => {\n    layout.loadDtmVersion()\n})\n\n</script>\n\n<style lang=\"postcss\" scoped>\n.ant-layout.ant-layout-has-sider {\n    min-height: calc(100vh - 64px);\n}\n</style>\n"
  },
  {
    "path": "admin/src/layout/components/content.vue",
    "content": "<template>\n    <router-view v-slot=\"{Component}\">\n        <transition name=\"fade-transform\" mode=\"out-in\">\n            <keep-alive>\n                <div>\n                    <a-alert\n                        v-if=\"errLines[0]\"\n                        type=\"error\"\n                        closable\n                        @close=\"onClose\"\n                    >\n                        <template #description>\n                            <span v-for=\"(ln, index) of errLines\" :key=\"index\">{{ ln }} <br> </span>\n                        </template>\n                    </a-alert>\n                    <component :is=\"Component\" :key=\"key\" />\n                </div>\n            </keep-alive>\n        </transition>\n    </router-view>\n</template>\n<script setup lang='ts'>\nimport { computed } from 'vue'\nimport { useRoute } from 'vue-router'\nimport { sleep } from '/@/utils/util'\nimport { useLayoutStore } from '/@/store/modules/layout'\n\nconst route = useRoute()\n\nconst key = computed(() => route.path)\n\nconst layoutStore = useLayoutStore()\nconst errLines = computed(() => layoutStore.globalError.split('\\n'))\nconst onClose = async() => {\n    await sleep(1000)\n    layoutStore.setGlobalError('')\n}\n\n</script>\n"
  },
  {
    "path": "admin/src/layout/components/header.vue",
    "content": "<template>\n    <div>\n        <a-layout-header class=\"flex header\">\n            <div class=\"flex items-center h-16 logo\">\n                <svg-icon style=\"width: 36px; height: 36px; margin-right: 84px;\" icon-class=\"svg-logo\" />\n                <span class=\"text-lg text-gray-400\">DTM admin {{ version }}</span>\n            </div>\n            <a-menu\n                v-model:selectedKeys=\"activeMenu\"\n                theme=\"dark\"\n                mode=\"horizontal\"\n                :style=\"{ lineHeight: '64px' }\"\n                @select=\"onOpenChange\"\n            >\n                <a-menu-item v-for=\"v in getMenubar.menuList\" :key=\"v.path\">{{ v.meta.title }}</a-menu-item>\n            </a-menu>\n        </a-layout-header>\n    </div>\n</template>\n<script setup lang='ts'>\nimport { ref } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport { useLayoutStore } from '/@/store/modules/layout'\n\nconst route = useRoute()\nconst router = useRouter()\nconst { getMenubar } = useLayoutStore()\nconst firstRedirectPath = '/admin'\nconst version = import.meta.env.VITE_ADMIN_VERSION\n\nconst activeMenu = ref([route.meta.activeMenu !== firstRedirectPath ? route.meta.activeMenu : '/'])\n\nconst onOpenChange = (d:any) => {\n    router.push({ path: d.key })\n}\n</script>\n\n<style scoped>\n.logo {\n  margin-right: 20px;\n}\n</style>\n"
  },
  {
    "path": "admin/src/layout/components/sidebar.vue",
    "content": "<template>\n    <a-menu\n        v-model:selectedKeys=\"activeMenu\"\n        v-model:openKeys=\"openKeys\"\n        mode=\"inline\"\n        :style=\"{ height: '100%', borderRight: 0 }\"\n        @select=\"onOpenChange\"\n    >\n        <a-sub-menu v-for=\"v in filterSubMenubarData\" :key=\"v.path\">\n            <template #title>\n                <span>\n                    {{ v.meta.title }}\n                </span>\n            </template>\n            <a-menu-item v-for=\"vv in v.children\" :key=\"vv.path\">{{ vv.meta.title }}</a-menu-item>\n        </a-sub-menu>\n    </a-menu>\n</template>\n\n<script setup lang='ts'>\nimport { computed } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport { useLayoutStore } from '/@/store/modules/layout'\nimport { IMenubarList } from '/@/type/store/layout'\nimport { findCurrentMenubar } from '/@/utils/util'\n\nconst route = useRoute()\nconst router = useRouter()\nconst { getMenubar } = useLayoutStore()\n\nconst filterSubMenubarData = computed(() => {\n    return findCurrentMenubar(getMenubar.menuList) as IMenubarList[]\n}) \n\nconst activeMenu = computed({\n    get: () => {\n        return [route.path]\n    },\n    // eslint-disable-next-line @typescript-eslint/no-unused-vars\n    set: (val) => {\n        // do nothing, just for eliminate warn\n    }\n})\n\nconst openKeys = computed({\n    get: () => {\n        const pos = route.path.lastIndexOf('/')\n        return [route.path.substring(0, pos)]\n    }, \n    // eslint-disable-next-line @typescript-eslint/no-unused-vars\n    set: (val) => {\n        // do onthing, just for eliminate warn\n    }\n})\n\nconst onOpenChange = (d:any) => {\n    router.push({ path: d.key })\n}\n</script>\n"
  },
  {
    "path": "admin/src/layout/index.vue",
    "content": "<script setup lang=\"ts\">\nimport Header from './components/header.vue'\n</script>\n\n<template>\n    <a-layout>\n        <a-layout-header class=\"header\">\n            <Header />\n        </a-layout-header>\n        <router-view />\n    </a-layout>\n</template>\n"
  },
  {
    "path": "admin/src/main.ts",
    "content": "import { createApp } from 'vue'\nimport App from './App.vue'\nimport router from '/@/router/index'\nimport { pinia } from '/@/store'\nimport { useLayoutStore } from '/@/store/modules/layout'\nimport '/@/permission'\n\nimport 'ant-design-vue/dist/antd.css'\nimport '/@/assets/css/index.css'\nimport 'virtual:svg-icons-register'\n\nconst app = createApp(App)\napp.use(router)\napp.use(pinia)\napp.mount('#app')\n\nwindow.onunhandledrejection = (ev: PromiseRejectionEvent) => {\n    showAlert(ev.reason.stack || ev.reason.message)\n}\nwindow.onerror = err => {\n    if (typeof err === 'string') {\n        return showAlert(err)\n    }\n    showAlert(JSON.stringify(err))\n}\n\nfunction showAlert(msg: string) {\n    const layout = useLayoutStore()\n    if (!layout.globalError) {\n        layout.setGlobalError(msg)\n    }\n}\n"
  },
  {
    "path": "admin/src/permission.ts",
    "content": "import router from '/@/router'\nimport { configure, start, done } from 'nprogress'\nimport { useLayoutStore } from './store/modules/layout'\n\nconfigure({ showSpinner: false })\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst defaultRoutePath = '/'\n\nrouter.beforeEach((to) => {\n    start()\n\n    const { getMenubar, concatAllowRoutes } = useLayoutStore()\n\n    if (getMenubar.menuList.length === 0) {\n        concatAllowRoutes()\n\n        return to.fullPath\n    }\n})\n\nrouter.afterEach(() => {\n    done()\n})\n"
  },
  {
    "path": "admin/src/router/asyncRouter.ts",
    "content": "const modules = import.meta.glob('../views/**/**.vue')\nconst components: IObject<() => Promise<typeof import('*.vue')>> = {\n    LayoutHeader: (() => import('/@/layout/index.vue')) as unknown as () => Promise<typeof import('*.vue')>\n}\n\nObject.keys(modules).forEach(key => {\n    const nameMatch = key.match(/^\\.\\.\\/views\\/(.+)\\.vue/)\n    if (!nameMatch) return\n    if (nameMatch[1].includes('_Components')) return\n    const indexMatch = nameMatch[1].match(/(.*)\\/Index$/i)\n    let name = indexMatch ? indexMatch[1] : nameMatch[1];\n    [name] = name.split('/').splice(-1)\n    components[name] = modules[key] as () => Promise<typeof import('*.vue')>\n})\n\nexport {\n    components\n}\n"
  },
  {
    "path": "admin/src/router/index.ts",
    "content": "import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'\nimport { IMenubarList } from '../type/store/layout'\nimport { components } from './asyncRouter'\n\nconst Components: IObject<() => Promise<typeof import('*.vue')>> =\n  Object.assign({}, components, {\n      LayoutHeader: (() =>\n          import('/@/layout/index.vue')) as unknown as () => Promise<\n      typeof import('*.vue')\n    >,\n      LayoutMain: (() =>\n          import('/@/layout/aside.vue')) as unknown as () => Promise<\n      typeof import('*.vue')\n    >\n  })\n\nexport const allowRouter: Array<IMenubarList> = [\n    {\n        name: 'Admin',\n        path: '/',\n        redirect: '/admin/global-transactions/all',\n        component: Components['LayoutHeader'],\n        meta: { title: 'Admin', activeMenu: '/admin' },\n        children: [\n            {\n                //   name: 'Nodes',\n                //   path: '/admin/nodes',\n                //   component: Components['LayoutMain'],\n                //   meta: { title: 'Nodes' },\n                //   children: [\n                //     {\n                //       name: 'LivingNodes',\n                //       path: '/admin/nodes/living',\n                //       component: Components['LivingNodes'],\n                //       meta: { title: 'Living Nodes' },\n                //     }\n                //   ]\n                // }, {\n                name: 'GlobalTransactions',\n                path: '/admin/global-transactions',\n                component: Components['LayoutMain'],\n                meta: { title: 'Global Transactions' },\n                children: [\n                    {\n                        name: 'AllTransactions',\n                        path: '/admin/global-transactions/all',\n                        component: Components['AllTransactions'],\n                        meta: { title: 'All Transactions' }\n                        // }, {\n                        //   name: 'UnfinishedTransactions',\n                        //   path: '/admin/global-transactions/unfinished',\n                        //   component: Components['UnfinishedTransactions'],\n                        //   meta: { title: 'Unfinished Transactions' },\n                    }, \n                    {\n                        name: 'TransactionDetail',\n                        path: '/admin/global-transactions/detail/:gid',\n                        component: Components['DialogTransactionDetail'],\n                        meta: { title: 'Transaction Detail' }\n                    },\n                ]\n            },\n            {\n                name: 'KVPairs',\n                path: '/admin/kv',\n                component: Components['LayoutMain'],\n                meta: { title: 'Key-Value Pairs' },\n                children: [\n                    {\n                        name: 'Topics',\n                        path: '/admin/kv/topics',\n                        component: Components['Topics'],\n                        meta: { title: 'Topics' }\n                    }\n                ]\n            }\n        ]\n    }\n]\n\nconst router = createRouter({\n    history: createWebHistory(window.basePath || undefined),\n    routes: allowRouter as RouteRecordRaw[]\n})\n\nexport default router\n"
  },
  {
    "path": "admin/src/store/index.ts",
    "content": "import { createPinia } from 'pinia'\nexport const pinia = createPinia()\n"
  },
  {
    "path": "admin/src/store/modules/layout.ts",
    "content": "import { defineStore } from 'pinia'\nimport { allowRouter } from '/@/router'\nimport { ILayout, IMenubar, IMenubarList, IStatus } from '/@/type/store/layout'\nimport { getDtmVersion } from '/@/api/api_dtm'\n\nexport const useLayoutStore = defineStore({\n    id: 'layout',\n    state: (): ILayout => ({\n        menubar: {\n            menuList: []\n        },\n        status: {\n            isLoading: false\n        },\n        dtmVersion: '',\n        globalError: ''\n    }),\n    getters: {\n        getMenubar(): IMenubar {\n            return this.menubar\n        },\n        getStatus(): IStatus {\n            return this.status\n        }\n    },\n    actions: {\n        setRoutes(data: Array<IMenubarList>): void {\n            this.menubar.menuList = data\n        },\n        setGlobalError(err: string) {\n            this.globalError = err\n        },\n        concatAllowRoutes(): void {\n            allowRouter.reverse().forEach(v => this.menubar.menuList.unshift(v))\n        },\n        async loadDtmVersion(): Promise<void> {\n            const { data: { version } } = await getDtmVersion()\n            this.dtmVersion = version\n            console.log('dtm version: ', this.dtmVersion)\n        }\n    }\n})\n"
  },
  {
    "path": "admin/src/type/index.d.ts",
    "content": "export { }\ndeclare global {\n  interface IObject<T> {\n    [index: string]: T\n  }\n  interface ImportMetaEnv {\n    VITE_APP_TITLE: string\n    VITE_PORT: number\n    VITE_PROXY: string\n    VITE_ADMIN_VERSION: string\n  }\n  interface ITable<T = any> {\n    data: Array<T>\n    next_position: number,\n    size: number\n  }\n  interface Window {\n    basePath: string;\n  }\n}\n"
  },
  {
    "path": "admin/src/type/shim.vue.d.ts",
    "content": "declare module '*.vue' {\n  import { defineComponent } from 'vue'\n  const Component: ReturnType<typeof defineComponent>\n  export default Component\n}\n"
  },
  {
    "path": "admin/src/type/store/layout.ts",
    "content": "export interface IMenubar {\n  menuList: Array<IMenubarList>\n}\n\nexport interface ILayout {\n  menubar: IMenubar\n  status: IStatus\n  dtmVersion: string\n  globalError: string\n}\n\nexport interface IStatus {\n  isLoading: boolean\n}\n\nexport interface IMenubarList {\n  parentId?: number | string\n  id?: number | string\n  name: string\n  path: string\n  redirect?: string\n  meta: {\n    icon?: string\n    title: string\n    permission?: string[]\n    activeMenu?: string\n    hidden?: boolean\n    alwaysShow?: boolean\n  }\n  component: (() => Promise<typeof import('*.vue')>) | string\n  children?: Array<IMenubarList>\n}\n"
  },
  {
    "path": "admin/src/utils/request.ts",
    "content": "import axios from 'axios'\n\nconst request = axios.create({\n    timeout: 60000\n})\n\nexport default request\n"
  },
  {
    "path": "admin/src/utils/util.ts",
    "content": "import { useRoute } from 'vue-router'\nimport { IMenubarList } from '../type/store/layout'\n\nexport const findCurrentMenubar = (menuList: IMenubarList[], root?: boolean) => {\n    const route = useRoute()\n    let arr: IMenubarList[] | IMenubarList = []\n    for (let i = 0; i < menuList.length; i++) {\n        const v = menuList[i]\n        const usePath = v.meta.activeMenu || v.redirect || v.path\n        const pos = usePath.lastIndexOf('/')\n        const rootPath = pos == 0 ? usePath : usePath.substring(0, pos)\n        if (route.path.indexOf(rootPath) !== -1) {\n            if (!root) {\n                arr = v.children as IMenubarList[]\n            } else {\n                arr = v\n            }\n            break\n        }\n    }\n\n    return arr\n}\n\nexport const sleep = async(ms: number) => {\n    return new Promise(resolve => setTimeout(resolve, ms))\n}"
  },
  {
    "path": "admin/src/views/Dashboard/GlobalTransactions/AllTransactions.vue",
    "content": "<template>\n    <div>\n        <a-form\n            layout=\"inline\"\n            :model=\"{}\"\n            @finish=\"searchFinish\"\n        >\n            <a-form-item>\n                <a-input v-model:value=\"gid\" placeholder=\"gid\" />\n            </a-form-item>\n            <a-form-item>\n                <a-select\n                    ref=\"select\"\n                    v-model:value=\"status\"\n                    style=\"width: 200px\"\n                >\n                    <a-select-option value=\"\">-- Status --</a-select-option>\n                    <a-select-option value=\"prepared\">prepared</a-select-option>\n                    <a-select-option value=\"submitted\">submitted</a-select-option>\n                    <a-select-option value=\"succeed\">succeed</a-select-option>\n                    <a-select-option value=\"failed\">failed</a-select-option>\n                    <a-select-option value=\"aborting\">aborting</a-select-option>\n                </a-select>\n            </a-form-item>\n            <a-form-item>\n                <a-select\n                    ref=\"select\"\n                    v-model:value=\"transType\"\n                    style=\"width: 200px\"\n                >\n                    <a-select-option value=\"\">-- Trans Type --</a-select-option>\n                    <a-select-option value=\"workflow\">workflow</a-select-option>\n                    <a-select-option value=\"saga\">saga</a-select-option>     \n                    <a-select-option value=\"tcc\">tcc</a-select-option>       \n                    <a-select-option value=\"msg\">msg</a-select-option>   \n                    <a-select-option value=\"xa\">xa</a-select-option>                    \n                </a-select>\n            </a-form-item>  \n            <a-form-item>\n                <a-range-picker\n                    v-model:value=\"createTimeRange\"\n                    format=\"YYYY-MM-DD HH:mm:ss\"\n                    :placeholder =\"['CreateTime Start', 'CreateTime End']\"                    \n                />\n            </a-form-item>\n            <a-form-item>\n                <a-button\n                    type=\"primary\"\n                    html-type=\"submit\"\n                >\n                    Search\n                </a-button>\n            </a-form-item>\n        </a-form>\n    </div>\n    <a-divider />\n    <div>\n        <a-table :columns=\"columns\" :data-source=\"dataSource\" :loading=\"loading\" :pagination=\"false\" :scroll=\"{ x: true }\" size=\"small\" >\n            <template #bodyCell=\"{column, record}\">\n                <template v-if=\"column.key === 'status'\">\n                    <span>\n                        <a-tag\n                            :key=\"record.status\"\n                            :color=\"record.status === 'succeed' ? 'green' : 'volcano'\"\n                        >{{ record.status }}</a-tag>\n                    </span>\n                </template>\n                <template v-else-if=\"column.key === 'action'\">\n                    <span style=\"width: 90px; display: block;\">\n                        <a class=\"mr-2 font-medium\" @click=\"handleTransactionDetail(record.gid)\">Dialog</a>\n                        <a class=\"mr-2 font-medium\" target=\"_blank\" :href=\"'./detail/'+record.gid\">Page</a>                        \n                    </span>\n                </template>\n            </template>\n        </a-table>\n        <div v-if=\"canPrev || canNext\" class=\"flex justify-center mt-2 text-lg pager\">\n            <a-button type=\"text\" :disabled=\"!canPrev\" @click=\"handlePrevPage\">Previous</a-button>\n            <a-button type=\"text\" :disabled=\"!canNext\" @click=\"handleNextPage\">Next</a-button>\n        </div>\n\n        <DialogTransactionDetail ref=\"transactionDetail\" />\n    </div>\n</template>\n<script setup lang=\"ts\">\nimport { IListAllTransactionsReq, listAllTransactions } from '/@/api/api_dtm'\nimport { computed, ref } from 'vue-demi'\nimport { usePagination } from 'vue-request'\nimport DialogTransactionDetail from './DialogTransactionDetail.vue'\n\nconst gid = ref('')\nconst status = ref('')\nconst transType = ref('')\nconst createTimeRange = ref()\n\nconst searchFinish = function() {\n    curPage.value = 1    \n    innerSearch('')\n}\n\n\nconst innerSearch = function(position: string) {\n    const params = {\n        position: position,\n        gid: gid.value,\n        status: status.value,\n        transType: transType.value,\n        createTimeStart: createTimeRange.value? createTimeRange.value[0].valueOf(): '',\n        createTimeEnd: createTimeRange.value? createTimeRange.value[1].valueOf(): '',\n        limit: pageSize.value\n    }\n    run(params)\n}\n\nconst columns = [\n    {\n        title: 'Action',\n        key: 'action'\n    }, {\n        title: 'GID',\n        dataIndex: 'gid',\n        key: 'gid'\n    }, {\n        title: 'TransType',\n        dataIndex: 'trans_type',\n        key: 'trans_type'\n    }, {\n        title: 'Status',\n        dataIndex: 'status',\n        key: 'status'\n    }, {\n        title: 'Protocol',\n        dataIndex: 'protocol',\n        key: 'protocol'\n    }, {\n        title: 'CreateTime',\n        dataIndex: 'create_time',\n        key: 'create_time'\n    },{\n        title: 'UpdateTime',\n        dataIndex: 'update_time',\n        key: 'update_time'\n    },{\n        title: 'FinishTime',\n        dataIndex: 'finish_time',\n        key: 'finish_time'\n    },{\n        title: 'RollbackTime',\n        dataIndex: 'rollback_time',\n        key: 'rollback_time'\n    }, {\n        title: 'NextCronInterval',\n        dataIndex: 'next_cron_interval',\n        key: 'next_cron_interval'\n    }, {\n        title: 'NextCronTime',\n        dataIndex: 'next_cron_time',\n        key: 'next_cron_time'\n    },\n]\n\nconst pages = ref([''])\nconst curPage = ref(1)\n\nconst canPrev = computed(() => {\n    return curPage.value > 1\n})\n\nconst canNext = computed(() => {\n    return data.value?.data.next_position !== ''\n})\n\ntype Data = {\n    transactions: {\n        gid: string\n        trans_type: string\n        status: string\n        protocol: string\n        create_time: string\n    }[]\n    next_position: string\n}\n\nconst queryData = (params: IListAllTransactionsReq) => {\n    return listAllTransactions<Data>(params)\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst { data, run, current, loading, pageSize } = usePagination(queryData, {\n    defaultParams: [\n        {\n            limit: 10\n        }\n    ],\n    pagination: {\n        pageSizeKey: 'limit'\n    }\n})\n\nconst dataSource = computed(() => data.value?.data.transactions || [])\n\nconst handlePrevPage = () => {\n    curPage.value -= 1\n    let position = pages.value[curPage.value] as string;\n    innerSearch(position);\n}\n\nconst handleNextPage = () => {\n    curPage.value += 1\n    pages.value[curPage.value] = data.value?.data.next_position as string\n\n    let position = data.value?.data.next_position || '';\n    innerSearch(position);  \n}\n\nconst transactionDetail = ref<null | { open: (gid: string) => null }>(null)\nconst handleTransactionDetail = (gid: string) => {\n    transactionDetail.value?.open(gid)\n}\n\n</script>\n\n<style lang=\"postcss\" scoped>\n::v-deep .ant-pagination-item {\n  display: none;\n}\n\n.pager .ant-btn-text {\n  font-weight: 500;\n}\n\n.pager .ant-btn {\n  padding: 6px;\n}\n</style>\n"
  },
  {
    "path": "admin/src/views/Dashboard/GlobalTransactions/DialogTransactionDetail.vue",
    "content": "<template>\n    <div>\n        <a-modal v-model:visible=\"visible\" :closable=\"closeable\" title=\"Transaction Detail\" width=\"100%\" wrap-class-name=\"full-modal\">\n            <template #footer>\n                <a-button type=\"primary\" @click=\"close\" v-if=\"closeable\">Close</a-button>\n            </template>            \n            <h2>Transaction Info</h2> \n            <a-button type=\"primary\" @click=\"refresh\" :loading=\"loading\" class=\"action-button\">Refresh</a-button>      \n            <a-popconfirm\n                title=\"Force stop it?\"\n                ok-text=\"Yes, stop it\"\n                ok-type=\"danger\"\n                cancel-text=\"No\"\n                class=\"action-button\"\n                :disabled=\"transaction?.status==='failed' || transaction?.status==='succeed'\"     \n                @confirm=\"handleTransactionStop(<string>transaction?.gid)\"                            \n            >\n                <a-button danger type=\"default\" :disabled=\"transaction?.status==='failed' || transaction?.status==='succeed'\"                                          \n                >ForceStop</a-button>\n            </a-popconfirm>\n            <!-- todo enable condition -->\n            <a-popconfirm\n                title=\"Reset next cron time to current time?\"\n                ok-text=\"Yes, reset\"                \n                cancel-text=\"No\"\n                class=\"action-button\"                    \n                @confirm=\"handleSetNextCronTimeToNow(<string>transaction?.gid)\"            >\n                <a-button type=\"default\">Reset next cron time</a-button>\n            </a-popconfirm>\n            <a-descriptions bordered size=\"small\" :column=\"{ xxl: 4, xl: 3, lg: 3, md: 3, sm: 2, xs: 1 }\">\n                <a-descriptions-item label=\"Status\">                          \n                    <a-tag :color=\"transaction?.status === 'succeed' ? 'green' : 'volcano'\">{{ transaction?.status }}</a-tag>\n                </a-descriptions-item>\n                <a-descriptions-item label=\"Id\">{{ transaction?.id }}</a-descriptions-item>\n                <a-descriptions-item label=\"GID\">{{ transaction?.gid }}</a-descriptions-item>\n                <a-descriptions-item label=\"TransType\">{{ transaction?.trans_type }}</a-descriptions-item> \n                <a-descriptions-item label=\"Protocol\">{{ transaction?.protocol }}</a-descriptions-item> \n                <a-descriptions-item label=\"CreateTime\">{{ transaction?.create_time }}</a-descriptions-item> \n                <a-descriptions-item label=\"FinishTime\">{{ transaction?.finish_time }}</a-descriptions-item> \n                <a-descriptions-item label=\"UpdateTime\">{{ transaction?.update_time }}</a-descriptions-item>                 \n                <a-descriptions-item label=\"NextCronInterval\">{{ transaction?.next_cron_interval }}</a-descriptions-item> \n                <a-descriptions-item label=\"NextCronTime\">{{ transaction?.next_cron_time }}</a-descriptions-item> \n                <a-descriptions-item label=\"RollbackReason\">{{ transaction?.rollback_reason }}</a-descriptions-item> \n            </a-descriptions>            \n            <h2>Branches</h2>\n            <a-table :columns=\"columns\" :data-source=\"dataSource\" :pagination=\"false\" :scroll=\"{ x: true}\">\n                <!-- eslint-disable-next-line vue/no-unused-vars -->\n                <template #bodyCell=\"{column, record}\" />\n            </a-table>\n            <div class=\"relative mt-10\">\n                <a-textarea id=\"qs\" v-model:value=\"textVal\" :auto-size=\"{ minRows: 10, maxRows: 10 }\" />\n                <screenfull class=\"absolute z-50 right-2 top-3\" identity=\"qs\" />\n            </div>\n        </a-modal>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport { getTransaction } from '/@/api/api_dtm'\nimport screenfull from '/@/components/Screenfull/index.vue'\nimport { useRoute } from 'vue-router';\nimport { string } from 'vue-types';\nimport { forceStopTransaction, resetNextCronTime } from '/@/api/api_dtm'\n// import VueJsonPretty from 'vue-json-pretty';\n// import 'vue-json-pretty/lib/styles.css'\nconst route = useRoute();\n\nconst loading = ref(false)\nconst dataSource = ref<Branches[]>([])\nconst transaction = ref<Transaction>()\nconst visible = ref(false)\nconst textVal = ref('')\nconst closeable = ref(true)\n\n\nlet _gid = <string>route.params.gid;\nconst open = async(gid: string) => {\n    _gid = gid;\n    loading.value = true;\n    const d = await getTransaction<Data>({ gid: gid })\n    dataSource.value = d.data.branches\n    transaction.value = d.data.transaction\n    textVal.value = JSON.stringify(d.data, null, 2)\n    visible.value = true\n    loading.value = false;\n}\nif(_gid) {\n    open(<string>route.params.gid);\n    closeable.value = false;\n}\n\nconst close = async() => {    \n    visible.value = false;\n}\n\nconst refresh =  async() => {    \n    open(_gid);\n}\n\nconst columns = [\n    {\n        title: 'BranchID',\n        dataIndex: 'branch_id',\n        key: 'branch_id'\n    }, {\n        title: 'Op',\n        dataIndex: 'op',\n        key: 'op'\n    }, {\n        title: 'Status',\n        dataIndex: 'status',\n        key: 'status'\n    }, {\n        title: 'CreateTime',\n        dataIndex: 'create_time',\n        key: 'create_time'\n    }, {\n        title: 'UpdateTime',\n        dataIndex: 'update_time',\n        key: 'update_time'\n    }, {\n        title: 'Url',\n        dataIndex: 'url',\n        key: 'url'\n    }\n]\n\nconst handleTransactionStop = async(gid: string) => {\n    await forceStopTransaction(gid);\n    refresh();\n}\n\n\nconst handleSetNextCronTimeToNow = async(gid: string) => {\n    await resetNextCronTime(gid);\n    refresh();\n}\n\ntype Data = {\n    branches: {\n        gid: string\n        branch_id: string\n        op: string\n        status: string\n        create_time: string\n        update_time: string\n        url: string\n    }[]\n    transaction: {\n        id: number\n        create_time: string\n        update_time: string\n        gid: string\n        trans_type: string\n        status: string\n        protocol: string\n        finish_time: string\n        options: string\n        next_cron_interval: number\n        next_cron_time: string\n        concurrent: boolean\n        rollback_reason: string\n    }\n}\n\ninterface Transaction  {\n    id: number\n    create_time: string\n    update_time: string\n    gid: string\n    trans_type: string\n    status: string\n    protocol: string\n    finish_time: string\n    options: string\n    next_cron_interval: number\n    next_cron_time: string\n    concurrent: boolean\n    rollback_reason: string\n}\n\ninterface Branches {\n    gid: string\n    branch_id: string\n    op: string\n    status: string\n    create_time: string\n    update_time: string\n    url: string\n}\n\ndefineExpose({\n    open\n})\n\n</script>\n\n<style lang=\"postcss\">\n.full-modal .ant-modal {\n    max-width: 100%;\n    top: 0;\n    padding-bottom: 0;\n    margin: 0;\n  }\n.full-modal .ant-modal-content {\n    display: flex;\n    flex-direction: column;\n    height: calc(100vh);\n  }\n.full-modal .ant-modal-body {\n    flex: 1;\n  }\n\n  .ant-modal {\n    height: 100%;\n  }\n.action-button {\n    margin-right: 10px;\n}\n</style>\n"
  },
  {
    "path": "admin/src/views/Dashboard/GlobalTransactions/UnfinishedTransactions.vue",
    "content": "<template>\n    <h1>Coming Soon</h1>\n</template>\n<script setup lang=\"ts\">\n</script>"
  },
  {
    "path": "admin/src/views/Dashboard/KVPairs/Topics.vue",
    "content": "<template>\n    <div>\n        <a-button type=\"primary\" class=\"mb-2\" @click=\"handleTopicSubscribe('')\">Subscribe</a-button>\n        <a-table :columns=\"columns\" :data-source=\"dataSource\" :loading=\"loading\" :pagination=\"false\">\n            <template #bodyCell=\"{column, record}\">\n                <template v-if=\"column.key === 'subscribers'\">\n                    <span>{{ JSON.parse(record.v).length }}</span>\n                </template>\n                <template v-if=\"column.key === 'action'\">\n                    <span>\n                        <a class=\"mr-2 font-medium\" @click=\"handleTopicSubscribe(record.k)\">Subscribe</a>\n                        <a class=\"mr-2 font-medium\" @click=\"handleTopicDetail(record.k,record.v)\">Detail</a>\n                        <a class=\"font-medium text-red-400\" @click=\"handleDeleteTopic(record.k)\">Delete</a>\n                    </span>\n                </template>\n            </template>\n        </a-table>\n        <div v-if=\"canPrev || canNext\" class=\"flex justify-center mt-2 text-lg pager\">\n            <a-button type=\"text\" :disabled=\"!canPrev\" @click=\"handlePrevPage\">Previous</a-button>\n            <a-button type=\"text\" :disabled=\"!canNext\" @click=\"handleNextPage\">Next</a-button>\n        </div>\n\n        <DialogTopicDetail ref=\"topicDetail\" @unsubscribed=\"handleRefreshData\" />\n        <DialogTopicSubscribe ref=\"topicSubscribe\" @subscribed=\"handleRefreshData\" />\n    </div>\n</template>\n<script setup lang=\"ts\">\nimport DialogTopicDetail from './_Components/DialogTopicDetail.vue'\nimport DialogTopicSubscribe from './_Components/DialogTopicSubscribe.vue'\nimport { deleteTopic, IListAllKVReq, listKVPairs } from '/@/api/api_dtm'\nimport { computed, ref } from 'vue-demi'\nimport { usePagination } from 'vue-request'\nimport { message, Modal } from 'ant-design-vue'\n\nconst columns = [\n    {\n        title: 'Name',\n        dataIndex: 'k',\n        key: 'name'\n    }, {\n        title: 'Subscribers',\n        dataIndex: 'v',\n        key: 'subscribers'\n    }, {\n        title: 'Version',\n        dataIndex: 'version',\n        key: 'version'\n    }, {\n        title: 'Action',\n        key: 'action'\n    }\n]\n\nconst pages = ref([''])\nconst curPage = ref(1)\n\nconst canPrev = computed(() => {\n    return curPage.value > 1\n})\n\nconst canNext = computed(() => {\n    return data.value?.data.next_position !== ''\n})\n\ntype Data = {\n    kv: {\n        k: string\n        v: string\n        version: number\n    }[]\n    next_position: string\n}\n\nconst queryData = (params: IListAllKVReq) => {\n    return listKVPairs<Data>(params)\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst { data, run, current, loading, pageSize } = usePagination(queryData, {\n    defaultParams: [\n        {\n            cat: 'topics',\n            limit: 10\n        }\n    ],\n    pagination: {\n        pageSizeKey: 'limit'\n    }\n})\n\nconst dataSource = computed(() => data.value?.data.kv || [])\n\nconst handlePrevPage = () => {\n    curPage.value -= 1\n    const params = {\n        cat: 'topics',\n        limit: pageSize.value,\n        position: pages.value[curPage.value] as string\n    }\n    run(params)\n}\n\nconst handleNextPage = () => {\n    curPage.value += 1\n    pages.value[curPage.value] = data.value?.data.next_position as string\n\n    run({\n        cat: 'topics',\n        position: data.value?.data.next_position,\n        limit: pageSize.value\n    })\n}\n\nconst handleRefreshData = () => {\n    run({ cat: 'topics', limit: pageSize.value })\n}\n\nconst handleDeleteTopic = (topic: string) => {\n    Modal.confirm({\n        title: 'Delete',\n        content: 'Do you want delete this topic? ',\n        okText: 'Yes',\n        okType: 'danger',\n        cancelText: 'Cancel',\n        onOk: async() => {\n            await deleteTopic(topic)\n            message.success('Delete topic succeed')\n            run({ cat: 'topics', limit: pageSize.value })\n        }\n    })\n}\n\nconst topicDetail = ref<null | { open: (topic: string, subscribers: string) => null }>(null)\nconst handleTopicDetail = (topic: string, subscribers: string) => {\n    topicDetail.value?.open(topic, subscribers)\n}\n\nconst topicSubscribe = ref<null | { open: (topic: string) => null }>(null)\nconst handleTopicSubscribe = (topic: string) => {\n    topicSubscribe.value?.open(topic)\n}\n</script>\n\n<style lang=\"postcss\" scoped>\n::deep .ant-pagination-item {\n  display: none;\n}\n\n.pager .ant-btn-text {\n  font-weight: 500;\n}\n\n.pager .ant-btn {\n  padding: 6px;\n}\n</style>\n"
  },
  {
    "path": "admin/src/views/Dashboard/KVPairs/_Components/DialogTopicDetail.vue",
    "content": "<template>\n    <div>\n        <a-modal v-model:visible=\"visible\" :title=\"topicName\" width=\"100%\" wrap-class-name=\"full-modal\" :footer=\"null\">\n            <a-table :columns=\"columns\" :data-source=\"dataSource\" :pagination=\"false\">\n                <template #bodyCell=\"{column, record}\">\n                    <template v-if=\"column.key === 'action'\">\n                        <span>\n                            <a class=\"text-red-400 font-medium\" @click=\"handleUnsubscribe(record.url)\">Unsubscribe</a>\n                        </span>\n                    </template>\n                </template>\n            </a-table>\n            <!-- <div class=\"mt-10 relative\">\n          <a-textarea id=\"qs\" v-model:value=\"textVal\" :auto-size=\"{ minRows: 10, maxRows: 10 }\" />\n          <screenfull class=\"absolute right-2 top-3 z-50\" identity=\"qs\" />\n      </div> -->\n        </a-modal>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport { unsubscribe } from '/@/api/api_dtm'\nimport { message, Modal } from 'ant-design-vue'\n// import VueJsonPretty from 'vue-json-pretty';\n// import 'vue-json-pretty/lib/styles.css'\n\nconst dataSource = ref<Subscriber[]>([])\nconst visible = ref(false)\nconst topicName = ref<string>('')\n\nconst open = async(topic: string, subscribers: string) => {\n    dataSource.value = JSON.parse(subscribers)\n    topicName.value = topic\n    visible.value = true\n}\n\nconst columns = [\n    {\n        title: 'URL',\n        dataIndex: 'url',\n        key: 'url'\n    }, {\n        title: 'Remark',\n        dataIndex: 'remark',\n        key: 'remark'\n    }, {\n        title: 'Action',\n        key: 'action'\n    }\n]\n\ninterface Subscriber {\n    url: string\n    remark: string\n}\n\nconst handleUnsubscribe = async(url: string) => {\n    Modal.confirm({\n        title: 'Unsubscribe',\n        content: 'Do you want unsubscribe this topic?',\n        okText: 'Yes',\n        okType: 'danger',\n        cancelText: 'Cancel',\n        onOk: async() => {\n            await unsubscribe({\n                topic: topicName.value,\n                url: url\n            })\n            message.success('Unsubscribe topic succeed')\n            location.reload()\n        }\n    })\n}\n\ndefineExpose({\n    open\n})\n\n</script>\n\n<style lang=\"postcss\">\n.full-modal .ant-modal {\n  max-width: 100%;\n  top: 0;\n  padding-bottom: 0;\n  margin: 0;\n}\n\n.full-modal .ant-modal-content {\n  display: flex;\n  flex-direction: column;\n  height: calc(100vh);\n}\n\n.full-modal .ant-modal-body {\n  flex: 1;\n}\n</style>\n"
  },
  {
    "path": "admin/src/views/Dashboard/KVPairs/_Components/DialogTopicSubscribe.vue",
    "content": "<template>\n    <div>\n        <a-modal\n            v-model:visible=\"visible\"\n            width=\"60%\"\n            title=\"Topic Subscribe\"\n            :confirm-loading=\"confirmLoading\"\n            @ok=\"handleSubscribe\"\n        >\n            <a-form v-bind=\"layout\" :mode=\"form\">\n                <a-form-item label=\"Topic: \">\n                    <a-input v-model:value=\"form.topic\" placeholder=\"Please input your topic...\" />\n                </a-form-item>\n                <a-form-item label=\"URL: \">\n                    <a-input v-model:value=\"form.url\" placeholder=\"Please input your url...\" />\n                </a-form-item>\n                <a-form-item label=\"Remark\">\n                    <a-textarea v-model:value=\"form.remark\" :rows=\"6\" placeholder=\"Please input your remark...\" />\n                </a-form-item>\n            </a-form>\n        </a-modal>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { message } from 'ant-design-vue'\nimport { reactive, ref } from 'vue'\nimport { subscribe } from '/@/api/api_dtm'\n\ninterface formState {\n    topic: string\n    url: string\n    remark: string\n}\n\nconst layout = {\n    labelCol: { span: 4 },\n    wrapperCol: { span: 16 }\n}\n\nconst form = reactive<formState>({\n    topic: '',\n    url: '',\n    remark: ''\n})\n\nconst visible = ref(false)\nconst open = async(topic: string) => {\n    form.topic = topic\n    visible.value = true\n}\n\nconst emit = defineEmits(['subscribed'])\n\nconst confirmLoading = ref<boolean>(false)\nconst handleSubscribe = async() => {\n    confirmLoading.value = true\n    await subscribe<string>(form).then(\n        () => {\n            visible.value = false\n            message.success('Subscribe succeed')\n            confirmLoading.value = false\n            emit('subscribed')\n        }\n    )\n        .catch(() => {\n            message.error('Failed')\n            confirmLoading.value = false\n            return\n        })\n}\n\ndefineExpose({\n    open\n})\n\n</script>\n"
  },
  {
    "path": "admin/src/views/Dashboard/Nodes/LivingNodes.vue",
    "content": "<template>\n    <h1>Coming Soon</h1>\n</template>\n\n<script setup lang=\"ts\">\n</script>"
  },
  {
    "path": "admin/tailwind.config.js",
    "content": "module.exports = {\n    content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],\n    theme: {\n        extend: {}\n    },\n    plugins: []\n}\n"
  },
  {
    "path": "admin/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"esnext\",\n    \"useDefineForClassFields\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"strict\": true,\n    \"jsx\": \"preserve\",\n    \"sourceMap\": true,\n    \"resolveJsonModule\": true,\n    \"esModuleInterop\": true,\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"/@/*\": [\"src/*\"],\n    },\n    \"lib\": [\"esnext\", \"dom\"],\n    \"types\": [\"vite/client\", \"node\"]\n  },\n  \"include\": [\"**/*.ts\", \"**/*.d.ts\", \"**/*.tsx\", \"**/*.vue\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "admin/vite.config.ts",
    "content": "import { ConfigEnv, UserConfigExport } from 'vite'\nimport path from 'path'\nimport vue from '@vitejs/plugin-vue'\nimport { createSvgIconsPlugin } from 'vite-plugin-svg-icons'\nimport Components from 'unplugin-vue-components/vite'\nimport { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'\nimport { ViteEjsPlugin } from 'vite-plugin-ejs'\nimport dns from 'dns'\n\nconst setAlias = (alias: [string, string][]) =>\n    alias.map((v) => {\n        return { find: v[0], replacement: path.resolve(__dirname, v[1]) }\n    })\n// https://cn.vitejs.dev/config/server-options.html#server-host\ndns.setDefaultResultOrder('verbatim')\n\nexport default ({ mode }: ConfigEnv): UserConfigExport => {\n    return {\n        resolve: {\n            alias: setAlias([['/@', 'src']])\n        },\n        plugins: [\n            vue(),\n            createSvgIconsPlugin({\n                iconDirs: [path.resolve(process.cwd(), 'src/icons')],\n                symbolId: 'icon-[dir]-[name]'\n            }),\n            Components({\n                dts: 'src/components.d.ts',\n                resolvers: [AntDesignVueResolver()]\n            }),\n            ViteEjsPlugin({\n                PUBLIC_PATH: mode !== 'development' ? 'PUBLIC-PATH-VARIABLE' : ''\n            })\n        ],\n        experimental: {\n            renderBuiltUrl(\n                filename: string,\n                {\n                    hostType\n                }: {\n          hostId: string;\n          hostType: 'js' | 'css' | 'html';\n          type: 'asset' | 'public';\n        }\n            ) {\n                if (hostType === 'js') {\n                    return {\n                        runtime: `window.__assetsPathBuilder(${JSON.stringify(filename)})`\n                    }\n                }\n\n                return filename\n            }\n        },\n        server: {\n            host: 'localhost',\n            port: 6789,\n            base: 'admin',\n            proxy: {\n                '/api': {\n                    changeOrigin: true,\n                    target: 'http://localhost:36789'\n\n                }\n            }\n        },\n        css: {\n            postcss: {\n                plugins: [\n                    require('autoprefixer'),\n                    require('tailwindcss'),\n                    require('postcss-simple-vars'),\n                    require('postcss-import')\n                ]\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "charts/.helmignore",
    "content": "# Patterns to ignore when building packages.\n# This supports shell glob matching, relative path matching, and\n# negation (prefixed with !). Only one pattern per line.\n.DS_Store\n# Common VCS dirs\n.git/\n.gitignore\n.bzr/\n.bzrignore\n.hg/\n.hgignore\n.svn/\n# Common backup files\n*.swp\n*.bak\n*.tmp\n*.orig\n*~\n# Various IDEs\n.project\n.idea/\n*.tmproj\n.vscode/\n"
  },
  {
    "path": "charts/Chart.yaml",
    "content": "apiVersion: v2\nname: dtm\ndescription: A Helm chart for Kubernetes\n\n# A chart can be either an 'application' or a 'library' chart.\n#\n# Application charts are a collection of templates that can be packaged into versioned archives\n# to be deployed.\n#\n# Library charts provide useful utilities or functions for the chart developer. They're included as\n# a dependency of application charts to inject those utilities and functions into the rendering\n# pipeline. Library charts do not define any templates and therefore cannot be deployed.\ntype: application\n\n# This is the chart version. This version number should be incremented each time you make changes\n# to the chart and its templates, including the app version.\n# Versions are expected to follow Semantic Versioning (https://semver.org/)\nversion: 0.1.0\n\n# This is the version number of the application being deployed. This version number should be\n# incremented each time you make changes to the application. Versions are not expected to\n# follow Semantic Versioning. They should reflect the version the application is using.\n# It is recommended to use it with quotes.\nappVersion: \"1.12.2\"\n"
  },
  {
    "path": "charts/README.md",
    "content": "# DTM charts\n\n## Usage\n\nInstall the dtm chart:\n\n```bash\nhelm install --create-namespace -n dtm-system dtm ./charts\n```\n\nUpgrade the dtm chart:\n\n```bash\nhelm upgrade -n dtm-system dtm ./charts\n```\n\nUninstall the chart:\n\n```bash\nhelm delete -n dtm-system dtm\n```\n\n## Parameters\n\n### Configuration parameters\n\n| Key             | Description                                                                                                                           | Value |\n|-----------------|---------------------------------------------------------------------------------------------------------------------------------------|-------|\n| `configuration` | DTM configuration. Specify content for `config.yaml`, ref: [sample config](https://github.com/dtm-labs/dtm/blob/main/conf.sample.yml) | `\"\"`  |\n\n\n\n### Autoscaling Parameters\n\n| Name                                            | Description                               | Value   |\n|-------------------------------------------------|-------------------------------------------|---------|\n| `autoscaling.enabled`                           | Enable Horizontal POD autoscaling for DTM | `false` |\n| `autoscaling.minReplicas`                       | Minimum number of DTM replicas            | `1`     |\n| `autoscaling.maxReplicas`                       | Maximum number of DTM replicas            | `10`    |\n| `autoscaling.targetCPUUtilizationPercentage`    | Target CPU utilization percentage         | `80`    |\n| `autoscaling.targetMemoryUtilizationPercentage` | Target Memory utilization percentage      | `80`    |\n\n### Ingress parameters\n\n| Key                            | Description                                                                   | Value               |\n|--------------------------------|-------------------------------------------------------------------------------|---------------------|\n| `ingress.enabled`              | Enable ingress record generation for DTM                                      | `false`             |\n| `ingress.className`            | IngressClass that will be used to implement the Ingress (Kubernetes 1.18+)    | `\"nginx\"`           |\n| `ingress.annotations`          | To enable certificate auto generation, place here your cert-manager annotations. | `{}`                |\n| `ingress.hosts.host`           | Default host for the ingress record.                                          | `\"your-domain.com\"` |\n| `ingress.hosts.paths.path`     | Default path for the ingress record                                           | `\"/\"`               |\n| `ingress.hosts.paths.pathType` | Ingress path type                                                             | `\"Prefix\"`          |\n| `ingress.tls`                  | Enable TLS configuration for the host defined at ingress.hostname parameter   | `[]`                |\n"
  },
  {
    "path": "charts/templates/NOTES.txt",
    "content": "1. Get the application URL by running these commands:\n{{- if .Values.ingress.enabled }}\n{{- range $host := .Values.ingress.hosts }}\n  {{- range .paths }}\n  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}\n  {{- end }}\n{{- end }}\n{{- else if contains \"NodePort\" .Values.service.type }}\n  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath=\"{.spec.ports[0].nodePort}\" services {{ include \"dtm.fullname\" . }})\n  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath=\"{.items[0].status.addresses[0].address}\")\n  echo http://$NODE_IP:$NODE_PORT\n{{- else if contains \"LoadBalancer\" .Values.service.type }}\n     NOTE: It may take a few minutes for the LoadBalancer IP to be available.\n           You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include \"dtm.fullname\" . }}'\n  export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include \"dtm.fullname\" . }} --template \"{{\"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}\"}}\")\n  echo http://$SERVICE_IP:{{ .Values.service.port }}\n{{- else if contains \"ClusterIP\" .Values.service.type }}\n  export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l \"app.kubernetes.io/name={{ include \"dtm.name\" . }},app.kubernetes.io/instance={{ .Release.Name }}\" -o jsonpath=\"{.items[0].metadata.name}\")\n  export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath=\"{.spec.containers[0].ports[0].containerPort}\")\n  echo \"Visit http://127.0.0.1:8080 to use your application\"\n  kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT\n{{- end }}\n"
  },
  {
    "path": "charts/templates/_helpers.tpl",
    "content": "{{/*\nExpand the name of the chart.\n*/}}\n{{- define \"dtm.name\" -}}\n{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix \"-\" }}\n{{- end }}\n\n{{/*\nCreate a default fully qualified app name.\nWe truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).\nIf release name contains chart name it will be used as a full name.\n*/}}\n{{- define \"dtm.fullname\" -}}\n{{- if .Values.fullnameOverride }}\n{{- .Values.fullnameOverride | trunc 63 | trimSuffix \"-\" }}\n{{- else }}\n{{- $name := default .Chart.Name .Values.nameOverride }}\n{{- if contains $name .Release.Name }}\n{{- .Release.Name | trunc 63 | trimSuffix \"-\" }}\n{{- else }}\n{{- printf \"%s-%s\" .Release.Name $name | trunc 63 | trimSuffix \"-\" }}\n{{- end }}\n{{- end }}\n{{- end }}\n\n{{/*\nCreate chart name and version as used by the chart label.\n*/}}\n{{- define \"dtm.chart\" -}}\n{{- printf \"%s-%s\" .Chart.Name .Chart.Version | replace \"+\" \"_\" | trunc 63 | trimSuffix \"-\" }}\n{{- end }}\n\n{{/*\nCommon labels\n*/}}\n{{- define \"dtm.labels\" -}}\nhelm.sh/chart: {{ include \"dtm.chart\" . }}\n{{ include \"dtm.selectorLabels\" . }}\n{{- if .Chart.AppVersion }}\napp.kubernetes.io/version: {{ .Chart.AppVersion | quote }}\n{{- end }}\napp.kubernetes.io/managed-by: {{ .Release.Service }}\n{{- end }}\n\n{{/*\nSelector labels\n*/}}\n{{- define \"dtm.selectorLabels\" -}}\napp.kubernetes.io/name: {{ include \"dtm.name\" . }}\napp.kubernetes.io/instance: {{ .Release.Name }}\n{{- end }}\n\n{{/*\nCreate the name of the service account to use\n*/}}\n{{- define \"dtm.serviceAccountName\" -}}\n{{- if .Values.serviceAccount.create }}\n{{- default (include \"dtm.fullname\" .) .Values.serviceAccount.name }}\n{{- else }}\n{{- default \"default\" .Values.serviceAccount.name }}\n{{- end }}\n{{- end }}\n"
  },
  {
    "path": "charts/templates/configmap.yaml",
    "content": "apiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: {{ include \"dtm.fullname\" . }}-conf\n  labels:\n    {{- include \"dtm.labels\" . | nindent 4 }}\ndata:\n  config.yaml: |-\n    {{- .Values.configuration | nindent 4 }}"
  },
  {
    "path": "charts/templates/deployment.yaml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: {{ include \"dtm.fullname\" . }}\n  labels:\n    {{- include \"dtm.labels\" . | nindent 4 }}\nspec:\n  {{- if not .Values.autoscaling.enabled }}\n  replicas: {{ .Values.replicaCount }}\n  {{- end }}\n  selector:\n    matchLabels:\n      {{- include \"dtm.selectorLabels\" . | nindent 6 }}\n  template:\n    metadata:\n      labels:\n        {{- include \"dtm.selectorLabels\" . | nindent 8 }}\n    spec:\n      {{- with .Values.imagePullSecrets }}\n      imagePullSecrets:\n        {{- toYaml . | nindent 8 }}\n      {{- end }}\n      securityContext:\n        {{- toYaml .Values.podSecurityContext | nindent 8 }}\n      containers:\n        - name: {{ .Chart.Name }}\n          securityContext:\n            {{- toYaml .Values.securityContext | nindent 12 }}\n          image: \"{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}\"\n          imagePullPolicy: {{ .Values.image.pullPolicy }}\n          args:\n            - \"-c=/app/dtm/configs/config.yaml\"\n          volumeMounts:\n            - mountPath: /app/dtm/configs\n              name: config\n          ports:\n            - containerPort: 36789\n              protocol: TCP\n              name: http\n            - containerPort: 36790\n              protocol: TCP\n              name: grpc\n          livenessProbe:\n            httpGet:\n              path: /api/ping\n              port: 36789\n              scheme: HTTP\n          readinessProbe:\n            httpGet:\n              path: /api/ping\n              port: 36789\n              scheme: HTTP\n          resources:\n            {{- toYaml .Values.resources | nindent 12 }}\n      {{- with .Values.nodeSelector }}\n      nodeSelector:\n        {{- toYaml . | nindent 8 }}\n      {{- end }}\n      {{- with .Values.affinity }}\n      affinity:\n        {{- toYaml . | nindent 8 }}\n      {{- end }}\n      {{- with .Values.tolerations }}\n      tolerations:\n        {{- toYaml . | nindent 8 }}\n      {{- end }}\n      volumes:\n        - name: config\n          configMap:\n            name: {{ include \"dtm.fullname\" . }}-conf\n"
  },
  {
    "path": "charts/templates/hpa.yaml",
    "content": "{{- if .Values.autoscaling.enabled }}\napiVersion: autoscaling/v2beta1\nkind: HorizontalPodAutoscaler\nmetadata:\n  name: {{ include \"dtm.fullname\" . }}\n  labels:\n    {{- include \"dtm.labels\" . | nindent 4 }}\nspec:\n  scaleTargetRef:\n    apiVersion: apps/v1\n    kind: Deployment\n    name: {{ include \"dtm.fullname\" . }}\n  minReplicas: {{ .Values.autoscaling.minReplicas }}\n  maxReplicas: {{ .Values.autoscaling.maxReplicas }}\n  metrics:\n    {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}\n    - type: Resource\n      resource:\n        name: cpu\n        targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}\n    {{- end }}\n    {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}\n    - type: Resource\n      resource:\n        name: memory\n        targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}\n    {{- end }}\n{{- end }}\n"
  },
  {
    "path": "charts/templates/ingress.yaml",
    "content": "{{- if .Values.ingress.enabled -}}\n{{- $fullName := include \"dtm.fullname\" . -}}\n{{- $svcPort := .Values.service.ports.http -}}\n{{- if and .Values.ingress.className (not (semverCompare \">=1.18-0\" .Capabilities.KubeVersion.GitVersion)) }}\n  {{- if not (hasKey .Values.ingress.annotations \"kubernetes.io/ingress.class\") }}\n  {{- $_ := set .Values.ingress.annotations \"kubernetes.io/ingress.class\" .Values.ingress.className}}\n  {{- end }}\n{{- end }}\n{{- if semverCompare \">=1.19-0\" .Capabilities.KubeVersion.GitVersion -}}\napiVersion: networking.k8s.io/v1\n{{- else if semverCompare \">=1.14-0\" .Capabilities.KubeVersion.GitVersion -}}\napiVersion: networking.k8s.io/v1beta1\n{{- else -}}\napiVersion: extensions/v1beta1\n{{- end }}\nkind: Ingress\nmetadata:\n  name: {{ $fullName }}\n  labels:\n    {{- include \"dtm.labels\" . | nindent 4 }}\n  {{- with .Values.ingress.annotations }}\n  annotations:\n    {{- toYaml . | nindent 4 }}\n  {{- end }}\nspec:\n  {{- if and .Values.ingress.className (semverCompare \">=1.18-0\" .Capabilities.KubeVersion.GitVersion) }}\n  ingressClassName: {{ .Values.ingress.className }}\n  {{- end }}\n  {{- if .Values.ingress.tls }}\n  tls:\n    {{- range .Values.ingress.tls }}\n    - hosts:\n        {{- range .hosts }}\n        - {{ . | quote }}\n        {{- end }}\n      secretName: {{ .secretName }}\n    {{- end }}\n  {{- end }}\n  rules:\n    {{- range .Values.ingress.hosts }}\n    - host: {{ .host | quote }}\n      http:\n        paths:\n          {{- range .paths }}\n          - path: {{ .path }}\n            {{- if and .pathType (semverCompare \">=1.18-0\" $.Capabilities.KubeVersion.GitVersion) }}\n            pathType: {{ .pathType }}\n            {{- end }}\n            backend:\n              {{- if semverCompare \">=1.19-0\" $.Capabilities.KubeVersion.GitVersion }}\n              service:\n                name: {{ $fullName }}\n                port:\n                  number: {{ $svcPort }}\n              {{- else }}\n              serviceName: {{ $fullName }}\n              servicePort: {{ $svcPort }}\n              {{- end }}\n          {{- end }}\n    {{- end }}\n{{- end }}\n"
  },
  {
    "path": "charts/templates/service.yaml",
    "content": "apiVersion: v1\nkind: Service\nmetadata:\n  name: {{ include \"dtm.fullname\" . }}\n  labels:\n    {{- include \"dtm.labels\" . | nindent 4 }}\nspec:\n  type: {{ .Values.service.type }}\n  ports:\n    - port: {{ .Values.service.ports.http }}\n      targetPort: http\n      protocol: TCP\n      name: http\n    - port: {{ .Values.service.ports.grpc }}\n      targetPort: grpc\n      protocol: TCP\n      name: grpc\n  selector:\n    {{- include \"dtm.selectorLabels\" . | nindent 4 }}\n"
  },
  {
    "path": "charts/templates/tests/test-connection.yaml",
    "content": "apiVersion: v1\nkind: Pod\nmetadata:\n  name: \"{{ include \"dtm.fullname\" . }}-test-connection\"\n  labels:\n    {{- include \"dtm.labels\" . | nindent 4 }}\n  annotations:\n    \"helm.sh/hook\": test\nspec:\n  containers:\n    - name: wget\n      image: busybox\n      command: ['wget']\n      args: ['{{ include \"dtm.fullname\" . }}:{{ .Values.service.port }}']\n  restartPolicy: Never\n"
  },
  {
    "path": "charts/values.yaml",
    "content": "# Default values for dtm.\n# This is a YAML-formatted file.\n# Declare variables to be passed into your templates.\n\n# DTM configuration. Specify content for config.yaml\n# ref: https://github.com/dtm-labs/dtm/blob/main/conf.sample.yml\nconfiguration: |-\n  Store: # specify which engine to store trans status\n    Driver: 'boltdb' # default store engine\n\n# replicaCount Number of dtm replicas to deploy\nreplicaCount: 1\n\n# dtm image version\nimage:\n  repository: yedf/dtm\n  tag: \"latest\"\n  pullPolicy: IfNotPresent\n\nimagePullSecrets: []\nnameOverride: \"\"\nfullnameOverride: \"\"\n\npodSecurityContext: {}\n  # fsGroup: 2000\n\nsecurityContext: {}\n  # capabilities:\n  #   drop:\n  #   - ALL\n  # readOnlyRootFilesystem: true\n  # runAsNonRoot: true\n  # runAsUser: 1000\n\nresources:\n  requests:\n    cpu: 200m\n    memory: 200Mi\n\nnodeSelector: {}\n\ntolerations: []\n\naffinity: {}\n\nservice:\n  type: ClusterIP\n  ports:\n    http: 36789\n    grpc: 36790\n\nautoscaling:\n  enabled: false\n  minReplicas: 1\n  maxReplicas: 10\n  targetCPUUtilizationPercentage: 80\n  targetMemoryUtilizationPercentage: 80\n\ningress:\n  enabled: false\n  className: \"nginx\"\n  annotations:\n    {}\n    # kubernetes.io/ingress.class: nginx\n    # kubernetes.io/tls-acme: \"true\"\n  hosts:\n    - host: your-domain.com\n      paths:\n        - path: /\n          pathType: Prefix\n  tls: []\n  #  - secretName: chart-example-tls\n  #    hosts:\n  #      - your-domain.com\n"
  },
  {
    "path": "client/README.md",
    "content": "# Go Client for DTM\n\nThere are there packages:\n\n## workflow\nWorkflow is a new client for DTM. It support the mixed usage of patterns saga, tcc, xa. And it also support the mixed usage of http, grpc and local transactions.\n\nThis pattern offers maximum flexibility and can handle a wide range of scenarios. This pattern is highly recommended for transactions that need to be rolled back\n\nQuick start for workflow using http can be found here: [https://github.com/dtm-labs/quick-start-sample/tree/main/workflow-http](https://github.com/dtm-labs/quick-start-sample/tree/main/workflow-http)\n\nQuick start for workflow using grpc can be found here: [https://github.com/dtm-labs/quick-start-sample/tree/main/workflow-grpc](https://github.com/dtm-labs/quick-start-sample/tree/main/workflow-grpc)\n\nDetailed examples can be found here: [https://github.com/dtm-labs/dtm-examples](https://github.com/dtm-labs/dtm-examples)\n\n\n## dtmcli\ndtmcli is the http client for patterns: saga, tcc, msg, xa\n\nQuick start for dtmcli can be found here: [https://github.com/dtm-labs/quick-start-sample/tree/main/dtmcli-qs](https://github.com/dtm-labs/quick-start-sample/tree/main/dtmcli-qs)\n\nDetailed examples can be found here: [https://github.com/dtm-labs/dtm-examples](https://github.com/dtm-labs/dtm-examples)\n\n## dtmgrpc\ndtmcli is the grpc client for patterns: saga, tcc, msg, xa\n\nQuick start for dtmgrpc can be found here: [https://github.com/dtm-labs/quick-start-sample/tree/main/dtmgrpc-qs](https://github.com/dtm-labs/quick-start-sample/tree/main/dtmgrpc-qs)\n\nDetailed examples can be found here: [https://github.com/dtm-labs/dtm-examples](https://github.com/dtm-labs/dtm-examples)\n\n\n"
  },
  {
    "path": "client/dtmcli/barrier.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmcli\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"net/url\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/logger\"\n)\n\n// BarrierBusiFunc type for busi func\ntype BarrierBusiFunc func(tx *sql.Tx) error\n\n// BranchBarrier every branch info\ntype BranchBarrier struct {\n\tTransType        string\n\tGid              string\n\tBranchID         string\n\tOp               string\n\tBarrierID        int\n\tDBType           string // DBTypeMysql | DBTypePostgres\n\tBarrierTableName string\n}\n\nfunc (bb *BranchBarrier) String() string {\n\treturn fmt.Sprintf(\"transInfo: %s %s %s %s\", bb.TransType, bb.Gid, bb.BranchID, bb.Op)\n}\n\nfunc (bb *BranchBarrier) newBarrierID() string {\n\tbb.BarrierID++\n\treturn fmt.Sprintf(\"%02d\", bb.BarrierID)\n}\n\n// BarrierFromQuery construct transaction info from request\nfunc BarrierFromQuery(qs url.Values) (*BranchBarrier, error) {\n\treturn BarrierFrom(dtmimp.EscapeGet(qs, \"trans_type\"), dtmimp.EscapeGet(qs, \"gid\"), dtmimp.EscapeGet(qs, \"branch_id\"), dtmimp.EscapeGet(qs, \"op\"))\n}\n\n// BarrierFrom construct transaction info from request\nfunc BarrierFrom(transType, gid, branchID, op string) (*BranchBarrier, error) {\n\tti := &BranchBarrier{\n\t\tTransType: transType,\n\t\tGid:       gid,\n\t\tBranchID:  branchID,\n\t\tOp:        op,\n\t}\n\tif ti.TransType == \"\" || ti.Gid == \"\" || ti.BranchID == \"\" || ti.Op == \"\" {\n\t\treturn nil, fmt.Errorf(\"invalid trans info: %v\", ti)\n\t}\n\treturn ti, nil\n}\n\n// Call see detail description in https://en.dtm.pub/practice/barrier.html\n// tx: local transaction connection\n// busiCall: busi func\nfunc (bb *BranchBarrier) Call(tx *sql.Tx, busiCall BarrierBusiFunc) (rerr error) {\n\tbid := bb.newBarrierID()\n\tdefer dtmimp.DeferDo(&rerr, func() error {\n\t\treturn tx.Commit()\n\t}, func() error {\n\t\treturn tx.Rollback()\n\t})\n\toriginOp := map[string]string{\n\t\tdtmimp.OpCancel:     dtmimp.OpTry,    // tcc\n\t\tdtmimp.OpCompensate: dtmimp.OpAction, // saga\n\t\tdtmimp.OpRollback:   dtmimp.OpAction, // workflow\n\t}[bb.Op]\n\n\toriginAffected, oerr := dtmimp.InsertBarrier(tx, bb.TransType, bb.Gid, bb.BranchID, originOp, bid, bb.Op, bb.DBType, bb.BarrierTableName)\n\tcurrentAffected, rerr := dtmimp.InsertBarrier(tx, bb.TransType, bb.Gid, bb.BranchID, bb.Op, bid, bb.Op, bb.DBType, bb.BarrierTableName)\n\tlogger.Debugf(\"originAffected: %d currentAffected: %d\", originAffected, currentAffected)\n\n\tif rerr == nil && bb.Op == dtmimp.MsgDoOp && currentAffected == 0 { // for msg's DoAndSubmit, repeated insert should be rejected.\n\t\treturn ErrDuplicated\n\t}\n\n\tif rerr == nil {\n\t\trerr = oerr\n\t}\n\n\tif (bb.Op == dtmimp.OpCancel || bb.Op == dtmimp.OpCompensate || bb.Op == dtmimp.OpRollback) && originAffected > 0 || // null compensate\n\t\tcurrentAffected == 0 { // repeated request or dangled request\n\t\treturn\n\t}\n\tif rerr == nil {\n\t\trerr = busiCall(tx)\n\t}\n\treturn\n}\n\n// CallWithDB the same as Call, but with *sql.DB\nfunc (bb *BranchBarrier) CallWithDB(db *sql.DB, busiCall BarrierBusiFunc) error {\n\ttx, err := db.Begin()\n\tif err == nil {\n\t\terr = bb.Call(tx, busiCall)\n\t}\n\treturn err\n}\n\n// QueryPrepared queries prepared data\nfunc (bb *BranchBarrier) QueryPrepared(db *sql.DB) error {\n\t_, err := dtmimp.InsertBarrier(db, bb.TransType, bb.Gid, dtmimp.MsgDoBranch0, dtmimp.MsgDoOp, dtmimp.MsgDoBarrier1, dtmimp.OpRollback, bb.DBType, bb.BarrierTableName)\n\tvar reason string\n\tif err == nil {\n\t\tsql := fmt.Sprintf(\"select reason from %s where gid=? and branch_id=? and op=? and barrier_id=?\", dtmimp.BarrierTableName)\n\t\tsql = dtmimp.GetDBSpecial(bb.DBType).GetPlaceHoldSQL(sql)\n\t\tlogger.Debugf(\"queryrow: %s\", sql, bb.Gid, dtmimp.MsgDoBranch0, dtmimp.MsgDoOp, dtmimp.MsgDoBarrier1)\n\t\terr = db.QueryRow(sql, bb.Gid, dtmimp.MsgDoBranch0, dtmimp.MsgDoOp, dtmimp.MsgDoBarrier1).Scan(&reason)\n\t}\n\tif reason == dtmimp.OpRollback {\n\t\treturn ErrFailure\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "client/dtmcli/barrier_mongo.go",
    "content": "package dtmcli\n\nimport (\n\t\"context\"\n\t\"strings\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/logger\"\n\t\"go.mongodb.org/mongo-driver/bson\"\n\t\"go.mongodb.org/mongo-driver/mongo\"\n)\n\n// MongoCall sub-trans barrier for mongo. see http://dtm.pub/practice/barrier\n// experimental\nfunc (bb *BranchBarrier) MongoCall(mc *mongo.Client, busiCall func(mongo.SessionContext) error) (rerr error) {\n\tbid := bb.newBarrierID()\n\treturn mc.UseSession(context.Background(), func(sc mongo.SessionContext) (rerr error) {\n\t\trerr = sc.StartTransaction()\n\t\tif rerr != nil {\n\t\t\treturn nil\n\t\t}\n\t\tdefer dtmimp.DeferDo(&rerr, func() error {\n\t\t\treturn sc.CommitTransaction(sc)\n\t\t}, func() error {\n\t\t\treturn sc.AbortTransaction(sc)\n\t\t})\n\t\toriginOp := map[string]string{\n\t\t\tdtmimp.OpCancel:     dtmimp.OpTry,\n\t\t\tdtmimp.OpCompensate: dtmimp.OpAction,\n\t\t}[bb.Op]\n\n\t\toriginAffected, oerr := mongoInsertBarrier(sc, mc, bb.TransType, bb.Gid, bb.BranchID, originOp, bid, bb.Op)\n\t\tcurrentAffected, rerr := mongoInsertBarrier(sc, mc, bb.TransType, bb.Gid, bb.BranchID, bb.Op, bid, bb.Op)\n\t\tlogger.Debugf(\"originAffected: %d currentAffected: %d\", originAffected, currentAffected)\n\n\t\tif rerr == nil && bb.Op == dtmimp.MsgDoOp && currentAffected == 0 { // for msg's DoAndSubmit, repeated insert should be rejected.\n\t\t\treturn ErrDuplicated\n\t\t}\n\n\t\tif rerr == nil {\n\t\t\trerr = oerr\n\t\t}\n\t\tif (bb.Op == dtmimp.OpCancel || bb.Op == dtmimp.OpCompensate) && originAffected > 0 || // null compensate\n\t\t\tcurrentAffected == 0 { // repeated request or dangled request\n\t\t\treturn\n\t\t}\n\t\tif rerr == nil {\n\t\t\trerr = busiCall(sc)\n\t\t}\n\t\treturn\n\t})\n}\n\n// MongoQueryPrepared query prepared for redis\n// experimental\nfunc (bb *BranchBarrier) MongoQueryPrepared(mc *mongo.Client) error {\n\t_, err := mongoInsertBarrier(context.Background(), mc, bb.TransType, bb.Gid, dtmimp.MsgDoBranch0, dtmimp.MsgDoOp, dtmimp.MsgDoBarrier1, dtmimp.OpRollback)\n\tvar result bson.M\n\tif err == nil {\n\t\tfs := strings.Split(dtmimp.BarrierTableName, \".\")\n\t\tbarrier := mc.Database(fs[0]).Collection(fs[1])\n\t\terr = barrier.FindOne(context.Background(), bson.D{\n\t\t\t{Key: \"gid\", Value: bb.Gid},\n\t\t\t{Key: \"branch_id\", Value: dtmimp.MsgDoBranch0},\n\t\t\t{Key: \"op\", Value: dtmimp.MsgDoOp},\n\t\t\t{Key: \"barrier_id\", Value: dtmimp.MsgDoBarrier1},\n\t\t}).Decode(&result)\n\t}\n\tvar reason string\n\tif err == nil {\n\t\treason, _ = result[\"reason\"].(string)\n\t}\n\tif err == nil && reason == dtmimp.OpRollback {\n\t\treturn ErrFailure\n\t}\n\treturn err\n}\n\nfunc mongoInsertBarrier(sc context.Context, mc *mongo.Client, transType string, gid string, branchID string, op string, barrierID string, reason string) (int64, error) {\n\tif op == \"\" {\n\t\treturn 0, nil\n\t}\n\tfs := strings.Split(dtmimp.BarrierTableName, \".\")\n\tbarrier := mc.Database(fs[0]).Collection(fs[1])\n\tr := barrier.FindOne(sc, bson.D{\n\t\t{Key: \"gid\", Value: gid},\n\t\t{Key: \"branch_id\", Value: branchID},\n\t\t{Key: \"op\", Value: op},\n\t\t{Key: \"barrier_id\", Value: barrierID},\n\t})\n\terr := r.Err()\n\tif err == mongo.ErrNoDocuments {\n\t\t_, err = barrier.InsertOne(sc,\n\t\t\tbson.D{\n\t\t\t\t{Key: \"trans_type\", Value: transType},\n\t\t\t\t{Key: \"gid\", Value: gid},\n\t\t\t\t{Key: \"branch_id\", Value: branchID},\n\t\t\t\t{Key: \"op\", Value: op},\n\t\t\t\t{Key: \"barrier_id\", Value: barrierID},\n\t\t\t\t{Key: \"reason\", Value: reason},\n\t\t\t})\n\t\treturn 1, err\n\t}\n\treturn 0, err\n}\n"
  },
  {
    "path": "client/dtmcli/barrier_redis.go",
    "content": "package dtmcli\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/redis/go-redis/v9\"\n)\n\n// RedisCheckAdjustAmount check the value of key is valid and >= amount. then adjust the amount\nfunc (bb *BranchBarrier) RedisCheckAdjustAmount(rd redis.Cmdable, key string, amount int, barrierExpire int) error {\n\tbid := bb.newBarrierID()\n\tbkey1 := fmt.Sprintf(\"%s-%s-%s-%s\", bb.Gid, bb.BranchID, bb.Op, bid)\n\toriginOp := map[string]string{\n\t\tdtmimp.OpCancel:     dtmimp.OpTry,\n\t\tdtmimp.OpCompensate: dtmimp.OpAction,\n\t}[bb.Op]\n\tbkey2 := fmt.Sprintf(\"%s-%s-%s-%s\", bb.Gid, bb.BranchID, originOp, bid)\n\tv, err := rd.Eval(context.Background(), ` -- RedisCheckAdjustAmount\nlocal v = redis.call('GET', KEYS[1])\nlocal e1 = redis.call('GET', KEYS[2])\n\nif v == false or v + ARGV[1] < 0 then\n\treturn 'FAILURE'\nend\n\nif e1 ~= false then\n\treturn 'DUPLICATE'\nend\n\nredis.call('SET', KEYS[2], 'op', 'EX', ARGV[3])\n\nif ARGV[2] ~= '' then\n\tlocal e2 = redis.call('GET', KEYS[3])\n\tif e2 == false then\n\t\tredis.call('SET', KEYS[3], 'rollback', 'EX', ARGV[3])\n\t\treturn\n\tend\nend\nredis.call('INCRBY', KEYS[1], ARGV[1])\n`, []string{key, bkey1, bkey2}, amount, originOp, barrierExpire).Result()\n\tlogger.Debugf(\"lua return v: %v err: %v\", v, err)\n\tif err == redis.Nil {\n\t\terr = nil\n\t}\n\tif err == nil && bb.Op == dtmimp.MsgDoOp && v == \"DUPLICATE\" { // msg DoAndSubmit should be rejected when duplicate\n\t\treturn ErrDuplicated\n\t}\n\tif err == nil && v == ResultFailure {\n\t\terr = ErrFailure\n\t}\n\treturn err\n}\n\n// RedisQueryPrepared query prepared for redis\nfunc (bb *BranchBarrier) RedisQueryPrepared(rd redis.Cmdable, barrierExpire int) error {\n\tbkey1 := fmt.Sprintf(\"%s-%s-%s-%s\", bb.Gid, dtmimp.MsgDoBranch0, dtmimp.MsgDoOp, dtmimp.MsgDoBarrier1)\n\tv, err := rd.Eval(context.Background(), ` -- RedisQueryPrepared\nlocal v = redis.call('GET', KEYS[1])\nif v == false then\n\tredis.call('SET', KEYS[1], 'rollback', 'EX', ARGV[1])\n\tv = 'rollback'\nend\nif v == 'rollback' then\n\treturn 'FAILURE'\nend\n`, []string{bkey1}, barrierExpire).Result()\n\tlogger.Debugf(\"lua return v: %v err: %v\", v, err)\n\tif err == redis.Nil {\n\t\terr = nil\n\t}\n\tif err == nil && v == ResultFailure {\n\t\terr = ErrFailure\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "client/dtmcli/consts.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmcli\n\nimport (\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n)\n\nconst (\n\t// StatusPrepared status for global/branch trans status.\n\t// first step, tx preparation period\n\tStatusPrepared = \"prepared\"\n\t// StatusSubmitted status for global trans status.\n\tStatusSubmitted = \"submitted\"\n\t// StatusSucceed status for global/branch trans status.\n\tStatusSucceed = \"succeed\"\n\t// StatusFailed status for global/branch trans status.\n\t// NOTE: change global status to failed can stop trigger (Not recommended in production env)\n\tStatusFailed = \"failed\"\n\t// StatusAborting status for global trans status.\n\tStatusAborting = \"aborting\"\n\n\t// ResultSuccess for result of a trans/trans branch\n\tResultSuccess = dtmimp.ResultSuccess\n\t// ResultFailure for result of a trans/trans branch\n\tResultFailure = dtmimp.ResultFailure\n\t// ResultOngoing for result of a trans/trans branch\n\tResultOngoing = dtmimp.ResultOngoing\n\n\t// DBTypeMysql const for driver mysql\n\tDBTypeMysql = dtmimp.DBTypeMysql\n\t// DBTypePostgres const for driver postgres\n\tDBTypePostgres = dtmimp.DBTypePostgres\n\t// DBTypeSQLServer const for driver SQLServer\n\tDBTypeSQLServer = dtmimp.DBTypeSQLServer\n)\n\n// MapSuccess HTTP result of SUCCESS\nvar MapSuccess = dtmimp.MapSuccess\n\n// MapFailure HTTP result of FAILURE\nvar MapFailure = dtmimp.MapFailure\n\n// ErrFailure error for returned failure\nvar ErrFailure = dtmimp.ErrFailure\n\n// ErrOngoing error for returned ongoing\nvar ErrOngoing = dtmimp.ErrOngoing\n\n// ErrDuplicated error of DUPLICATED for only msg\n// if QueryPrepared executed before call. then DoAndSubmit return this error\nvar ErrDuplicated = dtmimp.ErrDuplicated\n"
  },
  {
    "path": "client/dtmcli/cover_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmcli\n\nimport (\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestQuery(t *testing.T) {\n\tqs, err := url.ParseQuery(\"a=b\")\n\tassert.Nil(t, err)\n\t_, err = XaFromQuery(qs)\n\tassert.Error(t, err)\n\t_, err = TccFromQuery(qs)\n\tassert.Error(t, err)\n\t_, err = BarrierFromQuery(qs)\n\tassert.Error(t, err)\n}\n"
  },
  {
    "path": "client/dtmcli/dtmimp/README-cn.md",
    "content": "## 注意\n此包带imp后缀，主要被dtm内部使用，相关接口可能会发生变更，请勿使用这里的接口"
  },
  {
    "path": "client/dtmcli/dtmimp/README.md",
    "content": "## Notice\nPlease donot use this package, and this package should only be used in dtm internally. The interfaces are not stable, and package name has postfix \"imp\""
  },
  {
    "path": "client/dtmcli/dtmimp/consts.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmimp\n\nconst (\n\t// ResultFailure for result of a trans/trans branch\n\t// Same as HTTP status 409 and GRPC code 10\n\tResultFailure = \"FAILURE\"\n\t// ResultSuccess for result of a trans/trans branch\n\t// Same as HTTP status 200 and GRPC code 0\n\tResultSuccess = \"SUCCESS\"\n\t// ResultOngoing for result of a trans/trans branch\n\t// Same as HTTP status 425 and GRPC code 9\n\tResultOngoing = \"ONGOING\"\n\n\t// OpTry branch type for TCC\n\tOpTry = \"try\"\n\t// OpConfirm branch type for TCC\n\tOpConfirm = \"confirm\"\n\t// OpCancel branch type for TCC\n\tOpCancel = \"cancel\"\n\t// OpAction branch type for message, SAGA, XA\n\tOpAction = \"action\"\n\t// OpCompensate branch type for SAGA\n\tOpCompensate = \"compensate\"\n\t// OpCommit branch type for XA\n\tOpCommit = \"commit\"\n\t// OpRollback branch type for XA\n\tOpRollback = \"rollback\"\n\n\t// DBTypeMysql const for driver mysql\n\tDBTypeMysql = \"mysql\"\n\t// DBTypePostgres const for driver postgres\n\tDBTypePostgres = \"postgres\"\n\t// DBTypeSQLServer const for driver SQLServer\n\tDBTypeSQLServer = \"sqlserver\"\n\t// DBTypeRedis const for driver redis\n\tDBTypeRedis = \"redis\"\n\t// Jrpc const for json-rpc\n\tJrpc = \"json-rpc\"\n\t// JrpcCodeFailure const for json-rpc failure\n\tJrpcCodeFailure = -32901\n\n\t// JrpcCodeOngoing const for json-rpc ongoing\n\tJrpcCodeOngoing = -32902\n\n\t// MsgDoBranch0 const for DoAndSubmit barrier branch\n\tMsgDoBranch0 = \"00\"\n\t// MsgDoBarrier1 const for DoAndSubmit barrier barrierID\n\tMsgDoBarrier1 = \"01\"\n\t// MsgDoOp const for DoAndSubmit barrier op\n\tMsgDoOp = \"msg\"\n\t//MsgTopicPrefix const for Add topic msg\n\tMsgTopicPrefix = \"topic://\"\n\n\t// XaBarrier1 const for xa barrier id\n\tXaBarrier1 = \"01\"\n\n\t// ProtocolGRPC const for protocol grpc\n\tProtocolGRPC = \"grpc\"\n\t// ProtocolHTTP const for protocol http\n\tProtocolHTTP = \"http\"\n)\n"
  },
  {
    "path": "client/dtmcli/dtmimp/db_special.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmimp\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\n// DBSpecial db specific operations\ntype DBSpecial interface {\n\tGetPlaceHoldSQL(sql string) string\n\tGetInsertIgnoreTemplate(tableAndValues string, pgConstraint string) string\n\tGetXaSQL(command string, xid string) string\n}\n\nvar dbSpecials = map[string]DBSpecial{}\nvar currentDBType = DBTypeMysql\n\ntype mysqlDBSpecial struct{}\n\nfunc (*mysqlDBSpecial) GetPlaceHoldSQL(sql string) string {\n\treturn sql\n}\n\nfunc (*mysqlDBSpecial) GetXaSQL(command string, xid string) string {\n\tif command == \"abort\" {\n\t\tcommand = \"rollback\"\n\t}\n\treturn fmt.Sprintf(\"xa %s '%s'\", command, xid)\n}\n\nfunc (*mysqlDBSpecial) GetInsertIgnoreTemplate(tableAndValues string, pgConstraint string) string {\n\treturn fmt.Sprintf(\"insert ignore into %s\", tableAndValues)\n}\n\nfunc init() {\n\tdbSpecials[DBTypeMysql] = &mysqlDBSpecial{}\n}\n\ntype postgresDBSpecial struct{}\n\nfunc (*postgresDBSpecial) GetXaSQL(command string, xid string) string {\n\treturn map[string]string{\n\t\t\"end\":      \"\",\n\t\t\"start\":    \"begin\",\n\t\t\"abort\":    \"rollback\",\n\t\t\"prepare\":  fmt.Sprintf(\"prepare transaction '%s'\", xid),\n\t\t\"commit\":   fmt.Sprintf(\"commit prepared '%s'\", xid),\n\t\t\"rollback\": fmt.Sprintf(\"rollback prepared '%s'\", xid),\n\t}[command]\n}\n\nfunc (*postgresDBSpecial) GetPlaceHoldSQL(sql string) string {\n\tpos := 1\n\tparts := []string{}\n\tb := 0\n\tfor i := 0; i < len(sql); i++ {\n\t\tif sql[i] == '?' {\n\t\t\tparts = append(parts, sql[b:i])\n\t\t\tb = i + 1\n\t\t\tparts = append(parts, fmt.Sprintf(\"$%d\", pos))\n\t\t\tpos++\n\t\t}\n\t}\n\tparts = append(parts, sql[b:])\n\treturn strings.Join(parts, \"\")\n}\n\nfunc (*postgresDBSpecial) GetInsertIgnoreTemplate(tableAndValues string, pgConstraint string) string {\n\treturn fmt.Sprintf(\"insert into %s on conflict ON CONSTRAINT %s do nothing\", tableAndValues, pgConstraint)\n}\nfunc init() {\n\tdbSpecials[DBTypePostgres] = &postgresDBSpecial{}\n}\n\n// GetDBSpecial get DBSpecial for currentDBType\nfunc GetDBSpecial(dbType string) DBSpecial {\n\tif dbType == \"\" {\n\t\tdbType = currentDBType\n\t}\n\treturn dbSpecials[dbType]\n}\n\n// SetCurrentDBType set currentDBType\nfunc SetCurrentDBType(dbType string) {\n\tspec := dbSpecials[dbType]\n\tPanicIf(spec == nil, fmt.Errorf(\"unknown db type '%s'\", dbType))\n\tcurrentDBType = dbType\n}\n\n// GetCurrentDBType get currentDBType\nfunc GetCurrentDBType() string {\n\treturn currentDBType\n}\n"
  },
  {
    "path": "client/dtmcli/dtmimp/db_special_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmimp\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestDBSpecial(t *testing.T) {\n\told := currentDBType\n\tassert.Error(t, CatchP(func() {\n\t\tSetCurrentDBType(\"no-driver\")\n\t}))\n\tSetCurrentDBType(DBTypeMysql)\n\tsp := GetDBSpecial(DBTypeMysql)\n\n\tassert.Equal(t, \"? ?\", sp.GetPlaceHoldSQL(\"? ?\"))\n\tassert.Equal(t, \"xa start 'xa1'\", sp.GetXaSQL(\"start\", \"xa1\"))\n\tassert.Equal(t, \"insert ignore into a(f) values(?)\", sp.GetInsertIgnoreTemplate(\"a(f) values(?)\", \"c\"))\n\tSetCurrentDBType(DBTypePostgres)\n\tsp = GetDBSpecial(DBTypePostgres)\n\tassert.Equal(t, \"$1 $2\", sp.GetPlaceHoldSQL(\"? ?\"))\n\tassert.Equal(t, \"begin\", sp.GetXaSQL(\"start\", \"xa1\"))\n\tassert.Equal(t, \"insert into a(f) values(?) on conflict ON CONSTRAINT c do nothing\", sp.GetInsertIgnoreTemplate(\"a(f) values(?)\", \"c\"))\n\tSetCurrentDBType(old)\n}\n"
  },
  {
    "path": "client/dtmcli/dtmimp/trans_base.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmimp\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/go-resty/resty/v2\"\n)\n\n// BranchIDGen used to generate a sub branch id\ntype BranchIDGen struct {\n\tBranchID    string\n\tsubBranchID int\n}\n\n// NewSubBranchID generate a sub branch id\nfunc (g *BranchIDGen) NewSubBranchID() string {\n\tif g.subBranchID >= 99 {\n\t\tpanic(fmt.Errorf(\"branch id is larger than 99\"))\n\t}\n\tif len(g.BranchID) >= 20 {\n\t\tpanic(fmt.Errorf(\"total branch id is longer than 20\"))\n\t}\n\tg.subBranchID = g.subBranchID + 1\n\treturn g.CurrentSubBranchID()\n}\n\n// CurrentSubBranchID return current branchID\nfunc (g *BranchIDGen) CurrentSubBranchID() string {\n\treturn g.BranchID + fmt.Sprintf(\"%02d\", g.subBranchID)\n}\n\n// TransOptions transaction options\ntype TransOptions struct {\n\tWaitResult     bool              `json:\"wait_result,omitempty\" gorm:\"-\"`\n\tTimeoutToFail  int64             `json:\"timeout_to_fail,omitempty\" gorm:\"-\"` // for trans type: xa, tcc, unit: second\n\tRequestTimeout int64             `json:\"request_timeout,omitempty\" gorm:\"-\"` // for global trans resets request timeout, unit: second\n\tRetryInterval  int64             `json:\"retry_interval,omitempty\" gorm:\"-\"`  // for trans type: msg saga xa tcc, unit: second\n\tBranchHeaders  map[string]string `json:\"branch_headers,omitempty\" gorm:\"-\"`  // custom branch headers,  dtm server => service api\n\tConcurrent     bool              `json:\"concurrent\" gorm:\"-\"`                // for trans type: saga msg\n\tRetryLimit     int64             `json:\"retry_limit,omitempty\" gorm:\"-\"`     // for trans type: saga\n\tRetryCount     int64             `json:\"retry_count,omitempty\" gorm:\"-\"`     // for trans type: saga\n}\n\n// TransBase base for all trans\ntype TransBase struct {\n\tGid        string `json:\"gid\"` //  NOTE: unique in storage, can customize the generation rules instead of using server-side generation, it will help with the tracking\n\tTransType  string `json:\"trans_type\"`\n\tDtm        string `json:\"-\"`\n\tCustomData string `json:\"custom_data,omitempty\"` // nosql data persistence\n\tTransOptions\n\tContext context.Context `json:\"-\" gorm:\"-\"`\n\n\tSteps       []map[string]string `json:\"steps,omitempty\"`    // use in MSG/SAGA\n\tPayloads    []string            `json:\"payloads,omitempty\"` // used in MSG/SAGA\n\tBinPayloads [][]byte            `json:\"-\"`\n\tBranchIDGen `json:\"-\"`          // used in XA/TCC\n\tOp          string              `json:\"-\"` // used in XA/TCC\n\n\tQueryPrepared  string `json:\"query_prepared,omitempty\"` // used in MSG\n\tProtocol       string `json:\"protocol\"`\n\tRollbackReason string `json:\"rollback_reason,omitempty\" gorm:\"-\"`\n}\n\n// NewTransBase new a TransBase\nfunc NewTransBase(gid string, transType string, dtm string, branchID string) *TransBase {\n\treturn &TransBase{\n\t\tGid:         gid,\n\t\tTransType:   transType,\n\t\tBranchIDGen: BranchIDGen{BranchID: branchID},\n\t\tDtm:         dtm,\n\t\tContext:     context.Background(),\n\t}\n}\n\n// WithGlobalTransRequestTimeout defines global trans request timeout\nfunc (t *TransBase) WithGlobalTransRequestTimeout(timeout int64) {\n\tt.RequestTimeout = timeout\n}\n\n// WithRetryLimit defines global trans retry limit\nfunc (t *TransBase) WithRetryLimit(retryLimit int64) {\n\tt.RetryLimit = retryLimit\n}\n\n// TransBaseFromQuery construct transaction info from request\nfunc TransBaseFromQuery(qs url.Values) *TransBase {\n\treturn NewTransBase(EscapeGet(qs, \"gid\"), EscapeGet(qs, \"trans_type\"), EscapeGet(qs, \"dtm\"), EscapeGet(qs, \"branch_id\"))\n}\n\n// TransCallDtmExt TransBase call dtm\nfunc TransCallDtmExt(tb *TransBase, body interface{}, operation string) (*resty.Response, error) {\n\tif tb.Protocol == Jrpc {\n\t\treturn transCallDtmJrpc(tb, body, operation)\n\t}\n\trc := GetRestyClient2(time.Duration(tb.RequestTimeout) * time.Second)\n\tresp, err := rc.R().\n\t\tSetBody(body).Post(fmt.Sprintf(\"%s/%s\", tb.Dtm, operation))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif resp.StatusCode() != http.StatusOK || strings.Contains(resp.String(), ResultFailure) {\n\t\treturn nil, errors.New(resp.String())\n\t}\n\treturn resp, nil\n}\n\n// TransCallDtm is the short call for TransCallDtmExt\nfunc TransCallDtm(tb *TransBase, operation string) error {\n\t_, err := TransCallDtmExt(tb, tb, operation)\n\treturn err\n}\n\n// TransRegisterBranch TransBase register a branch to dtm\nfunc TransRegisterBranch(tb *TransBase, added map[string]string, operation string) error {\n\tm := map[string]string{\n\t\t\"gid\":        tb.Gid,\n\t\t\"trans_type\": tb.TransType,\n\t}\n\tfor k, v := range added {\n\t\tm[k] = v\n\t}\n\t_, err := TransCallDtmExt(tb, m, operation)\n\treturn err\n}\n\n// TransRequestBranch TransBase request branch result\nfunc TransRequestBranch(t *TransBase, method string, body interface{}, branchID string, op string, url string) (*resty.Response, error) {\n\tif url == \"\" {\n\t\treturn nil, nil\n\t}\n\tquery := map[string]string{\n\t\t\"dtm\":        t.Dtm,\n\t\t\"gid\":        t.Gid,\n\t\t\"branch_id\":  branchID,\n\t\t\"trans_type\": t.TransType,\n\t\t\"op\":         op,\n\t}\n\tif t.TransType == \"xa\" { // xa trans will add notify_url\n\t\tquery[\"phase2_url\"] = url\n\t}\n\tresp, err := GetRestyClient2(0).R().\n\t\tSetBody(body).\n\t\tSetQueryParams(query).\n\t\tSetHeaders(t.BranchHeaders).\n\t\tExecute(method, url)\n\treturn resp, err\n}\n\nfunc transCallDtmJrpc(tb *TransBase, body interface{}, operation string) (*resty.Response, error) {\n\trc := GetRestyClient2(time.Duration(tb.RequestTimeout) * time.Second)\n\tvar result map[string]interface{}\n\tresp, err := rc.R().\n\t\tSetBody(map[string]interface{}{\n\t\t\t\"jsonrpc\": \"2.0\",\n\t\t\t\"id\":      \"no-use\",\n\t\t\t\"method\":  operation,\n\t\t\t\"params\":  body,\n\t\t}).\n\t\tSetResult(&result).\n\t\tPost(tb.Dtm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif resp.StatusCode() != http.StatusOK || result[\"error\"] != nil {\n\t\treturn nil, errors.New(resp.String())\n\t}\n\treturn resp, nil\n}\n"
  },
  {
    "path": "client/dtmcli/dtmimp/trans_xa_base.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmimp\n\nimport (\n\t\"database/sql\"\n\t\"strings\"\n)\n\n// XaHandlePhase2 Handle the callback of commit/rollback\nfunc XaHandlePhase2(gid string, dbConf DBConf, branchID string, op string) error {\n\tdb, err := PooledDB(dbConf)\n\tif err != nil {\n\t\treturn err\n\t}\n\txaID := gid + \"-\" + branchID\n\t_, err = DBExec(dbConf.Driver, db, GetDBSpecial(dbConf.Driver).GetXaSQL(op, xaID))\n\tif err != nil &&\n\t\t(strings.Contains(err.Error(), \"XAER_NOTA\") || strings.Contains(err.Error(), \"does not exist\")) { // Repeat commit/rollback with the same id, report this error, ignore\n\t\terr = nil\n\t}\n\tif op == OpRollback && err == nil {\n\t\t// rollback insert a row after prepare. no-error means prepare has finished.\n\t\t_, err = InsertBarrier(db, \"xa\", gid, branchID, OpAction, XaBarrier1, op, dbConf.Driver, \"\")\n\t}\n\treturn err\n}\n\n// XaHandleLocalTrans public handler of LocalTransaction via http/grpc\nfunc XaHandleLocalTrans(xa *TransBase, dbConf DBConf, cb func(*sql.DB) error) (rerr error) {\n\txaBranch := xa.Gid + \"-\" + xa.BranchID\n\tdb, rerr := XaDB(dbConf)\n\tif rerr != nil {\n\t\treturn\n\t}\n\tdefer XaClose(db)\n\tdefer DeferDo(&rerr, func() error {\n\t\t_, err := DBExec(dbConf.Driver, db, GetDBSpecial(dbConf.Driver).GetXaSQL(\"prepare\", xaBranch))\n\t\treturn err\n\t}, func() error {\n\t\t_, err := DBExec(dbConf.Driver, db, GetDBSpecial(dbConf.Driver).GetXaSQL(\"abort\", xaBranch))\n\t\treturn err\n\t})\n\t_, rerr = DBExec(dbConf.Driver, db, GetDBSpecial(dbConf.Driver).GetXaSQL(\"start\", xaBranch))\n\tif rerr != nil {\n\t\treturn\n\t}\n\tdefer func() {\n\t\t_, _ = DBExec(dbConf.Driver, db, GetDBSpecial(dbConf.Driver).GetXaSQL(\"end\", xaBranch))\n\t}()\n\t// prepare and rollback both insert a row\n\t_, rerr = InsertBarrier(db, xa.TransType, xa.Gid, xa.BranchID, OpAction, XaBarrier1, OpAction, dbConf.Driver, \"\")\n\tif rerr == nil {\n\t\trerr = cb(db)\n\t}\n\treturn\n}\n\n// XaHandleGlobalTrans http/grpc GlobalTransaction shared func\nfunc XaHandleGlobalTrans(xa *TransBase, callDtm func(string) error, callBusi func() error) (rerr error) {\n\trerr = callDtm(\"prepare\")\n\tif rerr != nil {\n\t\treturn\n\t}\n\tdefer DeferDo(&rerr, func() error {\n\t\treturn callDtm(\"submit\")\n\t}, func() error {\n\t\treturn callDtm(\"abort\")\n\t})\n\trerr = callBusi()\n\treturn\n}\n"
  },
  {
    "path": "client/dtmcli/dtmimp/types.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmimp\n\nimport \"database/sql\"\n\n// DB interface of dtmcli db\ntype DB interface {\n\tExec(query string, args ...interface{}) (sql.Result, error)\n\tQueryRow(query string, args ...interface{}) *sql.Row\n}\n\n// DBConf defines db config\ntype DBConf struct {\n\tDriver   string `yaml:\"Driver\"`\n\tHost     string `yaml:\"Host\"`\n\tPort     int64  `yaml:\"Port\"`\n\tUser     string `yaml:\"User\"`\n\tPassword string `yaml:\"Password\"`\n\tDb       string `yaml:\"Db\"`\n\tSchema   string `yaml:\"Schema\"`\n}\n"
  },
  {
    "path": "client/dtmcli/dtmimp/types_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmimp\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestTypes(t *testing.T) {\n\terr := CatchP(func() {\n\t\tidGen := BranchIDGen{BranchID: \"12345678901234567890123\"}\n\t\tidGen.NewSubBranchID()\n\t})\n\tassert.Error(t, err)\n\terr = CatchP(func() {\n\t\tidGen := BranchIDGen{subBranchID: 99}\n\t\tidGen.NewSubBranchID()\n\t})\n\tassert.Error(t, err)\n}\n"
  },
  {
    "path": "client/dtmcli/dtmimp/utils.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmimp\n\nimport (\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"os\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/go-resty/resty/v2\"\n)\n\n// Logf an alias of Infof\n// Deprecated: use logger.Errorf\nvar Logf = logger.Infof\n\n// LogRedf an alias of Errorf\n// Deprecated: use logger.Errorf\nvar LogRedf = logger.Errorf\n\n// FatalIfError fatal if error is not nil\n// Deprecated: use logger.FatalIfError\nvar FatalIfError = logger.FatalIfError\n\n// LogIfFatalf fatal if cond is true\n// Deprecated: use logger.FatalfIf\nvar LogIfFatalf = logger.FatalfIf\n\n// AsError wrap a panic value as an error\nfunc AsError(x interface{}) error {\n\tlogger.Errorf(\"panic wrapped to error: '%v'\", x)\n\tif e, ok := x.(error); ok {\n\t\treturn e\n\t}\n\treturn fmt.Errorf(\"%v\", x)\n}\n\n// P2E panic to error\nfunc P2E(perr *error) {\n\tif x := recover(); x != nil {\n\t\t*perr = AsError(x)\n\t}\n}\n\n// E2P error to panic\nfunc E2P(err error) {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\n// CatchP catch panic to error\nfunc CatchP(f func()) (rerr error) {\n\tdefer P2E(&rerr)\n\tf()\n\treturn nil\n}\n\n// PanicIf name is clear\nfunc PanicIf(cond bool, err error) {\n\tif cond {\n\t\tpanic(err)\n\t}\n}\n\n// MustAtoi is string to int\nfunc MustAtoi(s string) int {\n\tr, err := strconv.Atoi(s)\n\tif err != nil {\n\t\tE2P(errors.New(\"convert to int error: \" + s))\n\t}\n\treturn r\n}\n\n// OrString return the first not empty string\nfunc OrString(ss ...string) string {\n\tfor _, s := range ss {\n\t\tif s != \"\" {\n\t\t\treturn s\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// If ternary operator\nfunc If(condition bool, trueObj interface{}, falseObj interface{}) interface{} {\n\tif condition {\n\t\treturn trueObj\n\t}\n\treturn falseObj\n}\n\n// MustMarshal checked version for marshal\nfunc MustMarshal(v interface{}) []byte {\n\tb, err := json.Marshal(v)\n\tE2P(err)\n\treturn b\n}\n\n// MustMarshalString string version of MustMarshal\nfunc MustMarshalString(v interface{}) string {\n\treturn string(MustMarshal(v))\n}\n\n// MustUnmarshal checked version for unmarshal\nfunc MustUnmarshal(b []byte, obj interface{}) {\n\terr := json.Unmarshal(b, obj)\n\tE2P(err)\n}\n\n// MustUnmarshalString string version of MustUnmarshal\nfunc MustUnmarshalString(s string, obj interface{}) {\n\tMustUnmarshal([]byte(s), obj)\n}\n\n// MustRemarshal marshal and unmarshal, and check error\nfunc MustRemarshal(from interface{}, to interface{}) {\n\tb, err := json.Marshal(from)\n\tE2P(err)\n\terr = json.Unmarshal(b, to)\n\tE2P(err)\n}\n\n// GetFuncName get current call func name\nfunc GetFuncName() string {\n\tpc, _, _, _ := runtime.Caller(1)\n\tnm := runtime.FuncForPC(pc).Name()\n\treturn nm[strings.LastIndex(nm, \".\")+1:]\n}\n\n// MayReplaceLocalhost when run in docker compose, change localhost to host.docker.internal for accessing host network\nfunc MayReplaceLocalhost(host string) string {\n\tif os.Getenv(\"IS_DOCKER\") != \"\" {\n\t\treturn strings.Replace(strings.Replace(host,\n\t\t\t\"localhost\", \"host.docker.internal\", 1),\n\t\t\t\"127.0.0.1\", \"host.docker.internal\", 1)\n\t}\n\treturn host\n}\n\nvar sqlDbs = &mapCache{cache: map[string]*sql.DB{}}\n\ntype mapCache struct {\n\tmutex sync.Mutex\n\tcache map[string]*sql.DB\n}\n\nfunc (m *mapCache) LoadOrStore(conf DBConf, factory func(conf DBConf) (*sql.DB, error)) (*sql.DB, error) {\n\tm.mutex.Lock()\n\tdefer m.mutex.Unlock()\n\tdsn := GetDsn(conf)\n\tif db, ok := m.cache[dsn]; ok {\n\t\treturn db, nil\n\t}\n\tdb, err := factory(conf)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tm.cache[dsn] = db\n\treturn db, nil\n}\n\n// PooledDB get pooled sql.DB\nfunc PooledDB(conf DBConf) (*sql.DB, error) {\n\treturn sqlDbs.LoadOrStore(conf, StandaloneDB)\n}\n\n// StandaloneDB get a standalone db instance\nfunc StandaloneDB(conf DBConf) (*sql.DB, error) {\n\tdsn := GetDsn(conf)\n\tlogger.Infof(\"opening standalone %s: %s\", conf.Driver, strings.Replace(dsn, conf.Password, \"****\", 1))\n\treturn sql.Open(conf.Driver, dsn)\n}\n\n// XaDB return a standalone db instance for xa\nfunc XaDB(conf DBConf) (*sql.DB, error) {\n\tdsn := GetDsn(conf)\n\tif conf.Driver == DBTypeMysql {\n\t\tdsn += \"&autocommit=0\"\n\t}\n\tlogger.Infof(\"opening xa standalone %s: %s\", conf.Driver, strings.Replace(dsn, conf.Password, \"****\", 1))\n\treturn sql.Open(conf.Driver, dsn)\n}\n\n// XaClose will log and close the db\nfunc XaClose(db *sql.DB) {\n\tlogger.Infof(\"closing xa db\")\n\t_ = db.Close()\n}\n\n// DBExec use raw db to exec\nfunc DBExec(dbType string, db DB, sql string, values ...interface{}) (affected int64, rerr error) {\n\tif sql == \"\" {\n\t\treturn 0, nil\n\t}\n\tbegan := time.Now()\n\tif len(values) > 0 {\n\t\tsql = GetDBSpecial(dbType).GetPlaceHoldSQL(sql)\n\t}\n\tr, rerr := db.Exec(sql, values...)\n\tused := time.Since(began) / time.Millisecond\n\tif rerr == nil {\n\t\taffected, rerr = r.RowsAffected()\n\t\tlogger.Debugf(\"used: %d ms affected: %d for %s %v\", used, affected, sql, values)\n\t} else {\n\t\tlogger.Errorf(\"used: %d ms exec error: %v for %s %v\", used, rerr, sql, values)\n\t}\n\treturn\n}\n\n// GetDsn get dsn from map config\nfunc GetDsn(conf DBConf) string {\n\thost := MayReplaceLocalhost(conf.Host)\n\tdriver := conf.Driver\n\tdsn := map[string]string{\n\t\t\"mysql\": fmt.Sprintf(\"%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=true&loc=Local&interpolateParams=true\",\n\t\t\tconf.User, conf.Password, host, conf.Port, conf.Db),\n\t\t\"postgres\": fmt.Sprintf(\"host=%s user=%s password=%s dbname='%s' search_path=%s port=%d sslmode=disable\",\n\t\t\thost, conf.User, conf.Password, conf.Db, conf.Schema, conf.Port),\n\t\t// sqlserver://sa:mypass@localhost:1234?database=master&connection+timeout=30\n\t\t\"sqlserver\": getSQLServerConnectionString(&conf, &host),\n\t}[driver]\n\tPanicIf(dsn == \"\", fmt.Errorf(\"unknow driver: %s\", driver))\n\treturn dsn\n}\n\nfunc getSQLServerConnectionString(conf *DBConf, host *string) string {\n\tquery := url.Values{}\n\tquery.Add(\"database\", conf.Db)\n\tu := &url.URL{\n\t\tScheme: \"sqlserver\",\n\t\tUser:   url.UserPassword(conf.User, conf.Password),\n\t\tHost:   fmt.Sprintf(\"%s:%d\", *host, conf.Port),\n\t\t// Path:  instance, // if connecting to an instance instead of a port\n\t\tRawQuery: query.Encode(),\n\t}\n\treturn u.String()\n}\n\n// RespAsErrorByJSONRPC  translate json rpc resty response to error\nfunc RespAsErrorByJSONRPC(resp *resty.Response) error {\n\tstr := resp.String()\n\tvar result map[string]interface{}\n\tMustUnmarshalString(str, &result)\n\tif result[\"error\"] != nil {\n\t\trerr := result[\"error\"].(map[string]interface{})\n\t\tif rerr[\"code\"] == JrpcCodeFailure {\n\t\t\treturn fmt.Errorf(\"%s. %w\", str, ErrFailure)\n\t\t} else if rerr[\"code\"] == JrpcCodeOngoing {\n\t\t\treturn ErrOngoing\n\t\t}\n\t\treturn errors.New(resp.String())\n\t}\n\treturn nil\n}\n\n// DeferDo a common defer do used in dtmcli/dtmgrpc\nfunc DeferDo(rerr *error, success func() error, fail func() error) {\n\tif x := recover(); x != nil {\n\t\t*rerr = AsError(x)\n\t\t_ = fail()\n\t\tpanic(x)\n\t} else if *rerr != nil {\n\t\t_ = fail()\n\t} else {\n\t\t*rerr = success()\n\t}\n}\n\n// Escape solve CodeQL reported problem\nfunc Escape(input string) string {\n\tv := strings.Replace(input, \"\\n\", \"\", -1)\n\tv = strings.Replace(v, \"\\r\", \"\", -1)\n\tv = strings.Replace(v, \";\", \"\", -1)\n\t// v = strings.Replace(v, \"'\", \"\", -1)\n\treturn v\n}\n\n// EscapeGet escape get\nfunc EscapeGet(qs url.Values, key string) string {\n\treturn Escape(qs.Get(key))\n}\n\n// InsertBarrier insert a record to barrier\nfunc InsertBarrier(tx DB, transType string, gid string, branchID string, op string, barrierID string, reason string, dbType string, barrierTableName string) (int64, error) {\n\tif op == \"\" {\n\t\treturn 0, nil\n\t}\n\tif dbType == \"\" {\n\t\tdbType = currentDBType\n\t}\n\tif barrierTableName == \"\" {\n\t\tbarrierTableName = BarrierTableName\n\t}\n\tsql := GetDBSpecial(dbType).GetInsertIgnoreTemplate(barrierTableName+\"(trans_type, gid, branch_id, op, barrier_id, reason) values(?,?,?,?,?,?)\", \"uniq_barrier\")\n\treturn DBExec(dbType, tx, sql, transType, gid, branchID, op, barrierID, reason)\n}\n"
  },
  {
    "path": "client/dtmcli/dtmimp/utils_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmimp\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestEP(t *testing.T) {\n\tskipped := true\n\terr := func() (rerr error) {\n\t\tdefer P2E(&rerr)\n\t\tE2P(errors.New(\"err1\"))\n\t\tskipped = false\n\t\treturn nil\n\t}()\n\tassert.Equal(t, true, skipped)\n\tassert.Equal(t, \"err1\", err.Error())\n\terr = CatchP(func() {\n\t\tPanicIf(true, errors.New(\"err2\"))\n\t})\n\tassert.Equal(t, \"err2\", err.Error())\n\terr = func() (rerr error) {\n\t\tdefer P2E(&rerr)\n\t\tpanic(\"raw_string\")\n\t}()\n\tassert.Equal(t, \"raw_string\", err.Error())\n}\n\nfunc TestTernary(t *testing.T) {\n\tassert.Equal(t, \"1\", OrString(\"\", \"\", \"1\"))\n\tassert.Equal(t, \"\", OrString(\"\", \"\", \"\"))\n\tassert.Equal(t, \"1\", If(true, \"1\", \"2\"))\n\tassert.Equal(t, \"2\", If(false, \"1\", \"2\"))\n}\n\nfunc TestMarshal(t *testing.T) {\n\ta := 0\n\ttype e struct {\n\t\tA int\n\t}\n\te1 := e{A: 10}\n\tm := map[string]int{}\n\tassert.Equal(t, \"1\", MustMarshalString(1))\n\tassert.Equal(t, []byte(\"1\"), MustMarshal(1))\n\tMustUnmarshal([]byte(\"2\"), &a)\n\tassert.Equal(t, 2, a)\n\tMustUnmarshalString(\"3\", &a)\n\tassert.Equal(t, 3, a)\n\tMustRemarshal(&e1, &m)\n\tassert.Equal(t, 10, m[\"A\"])\n}\n\nfunc TestSome(t *testing.T) {\n\tn := MustAtoi(\"123\")\n\tassert.Equal(t, 123, n)\n\n\terr := CatchP(func() {\n\t\tMustAtoi(\"abc\")\n\t})\n\tassert.Error(t, err)\n\n\tfunc1 := GetFuncName()\n\tassert.Equal(t, true, strings.HasSuffix(func1, \"TestSome\"))\n\n\tos.Setenv(\"IS_DOCKER\", \"1\")\n\ts := MayReplaceLocalhost(\"http://localhost\")\n\tassert.Equal(t, \"http://host.docker.internal\", s)\n\tos.Setenv(\"IS_DOCKER\", \"\")\n\ts2 := MayReplaceLocalhost(\"http://localhost\")\n\tassert.Equal(t, \"http://localhost\", s2)\n}\n"
  },
  {
    "path": "client/dtmcli/dtmimp/vars.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmimp\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtmdriver\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/go-resty/resty/v2\"\n)\n\n// ErrFailure error of FAILURE\nvar ErrFailure = errors.New(\"FAILURE\")\n\n// ErrOngoing error of ONGOING\nvar ErrOngoing = errors.New(\"ONGOING\")\n\n// ErrDuplicated error of DUPLICATED for only msg\n// if QueryPrepared executed before call. then DoAndSubmit return this error\nvar ErrDuplicated = errors.New(\"DUPLICATED\")\n\n// MapSuccess HTTP result of SUCCESS\nvar MapSuccess = map[string]interface{}{\"dtm_result\": ResultSuccess}\n\n// MapFailure HTTP result of FAILURE\nvar MapFailure = map[string]interface{}{\"dtm_result\": ResultFailure}\n\n// BarrierTableName the table name of barrier table\nvar BarrierTableName = \"dtm_barrier.barrier\"\n\nvar restyClients sync.Map\n\n// GetRestyClient2 will return a resty client with timeout set\nfunc GetRestyClient2(timeout time.Duration) *resty.Client {\n\tcli, ok := restyClients.Load(timeout)\n\tif !ok {\n\t\tclient := resty.New()\n\t\tif timeout != 0 {\n\t\t\tclient.SetTimeout(timeout)\n\t\t}\n\t\tAddRestyMiddlewares(client)\n\t\trestyClients.Store(timeout, client)\n\t\tcli = client\n\t}\n\treturn cli.(*resty.Client)\n}\n\n// AddRestyMiddlewares will add the middlewares used by dtm\nfunc AddRestyMiddlewares(client *resty.Client) {\n\tclient.OnBeforeRequest(func(c *resty.Client, r *resty.Request) error {\n\t\told := r.URL\n\t\tr.URL = MayReplaceLocalhost(r.URL)\n\t\tms := dtmdriver.Middlewares.HTTP\n\t\tvar err error\n\t\tfor i := 0; i < len(ms) && err == nil; i++ {\n\t\t\terr = ms[i](c, r)\n\t\t}\n\t\tlogger.Debugf(\"requesting: %s %s %s resolved: %s err: %v\", r.Method, old, MustMarshalString(r.Body), r.URL, err)\n\t\treturn err\n\t})\n\tclient.OnAfterResponse(func(c *resty.Client, resp *resty.Response) error {\n\t\tr := resp.Request\n\t\tlogger.Debugf(\"requested: %d %s %s %s\", resp.StatusCode(), r.Method, r.URL, resp.String())\n\t\treturn nil\n\t})\n}\n"
  },
  {
    "path": "client/dtmcli/logger/logger.go",
    "content": "package logger\n\nimport (\n\t\"github.com/dtm-labs/logger\"\n)\n\nvar (\n\t// WithLogger replaces default logger\n\tWithLogger = logger.WithLogger\n\t// InitLog is an initialization for a logger\n\t// level can be: debug info warn error\n\tInitLog = logger.InitLog\n\t// InitLog2 specify advanced log config\n\tInitLog2 = logger.InitLog2\n\t// Debugf log to level debug\n\tDebugf = logger.Debugf\n\n\t// Infof log to level info\n\tInfof = logger.Infof\n\n\t// Warnf log to level warn\n\tWarnf = logger.Warnf\n\t// Errorf log to level error\n\tErrorf = logger.Errorf\n\n\t// FatalfIf log to level error\n\tFatalfIf = logger.FatalfIf\n\n\t// FatalIfError if err is not nil, then log to level fatal and call os.Exit\n\tFatalIfError = logger.FatalIfError\n)\n"
  },
  {
    "path": "client/dtmcli/trans_msg.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmcli\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n)\n\n// Msg reliable msg type\ntype Msg struct {\n\tdtmimp.TransBase\n\tdelay uint64 // delay call branch, unit second\n}\n\n// NewMsg create new msg\nfunc NewMsg(server string, gid string) *Msg {\n\treturn &Msg{TransBase: *dtmimp.NewTransBase(gid, \"msg\", server, \"\")}\n}\n\n// Add add a new step\nfunc (s *Msg) Add(action string, postData interface{}) *Msg {\n\ts.Steps = append(s.Steps, map[string]string{\"action\": action})\n\ts.Payloads = append(s.Payloads, dtmimp.MustMarshalString(postData))\n\treturn s\n}\n\n// AddTopic add a new topic step\nfunc (s *Msg) AddTopic(topic string, postData interface{}) *Msg {\n\treturn s.Add(fmt.Sprintf(\"%s%s\", dtmimp.MsgTopicPrefix, topic), postData)\n}\n\n// SetDelay delay call branch, unit second\nfunc (s *Msg) SetDelay(delay uint64) *Msg {\n\ts.delay = delay\n\treturn s\n}\n\n// Prepare prepare the msg, msg will later be submitted\nfunc (s *Msg) Prepare(queryPrepared string) error {\n\ts.QueryPrepared = dtmimp.OrString(queryPrepared, s.QueryPrepared)\n\treturn dtmimp.TransCallDtm(&s.TransBase, \"prepare\")\n}\n\n// Submit submit the msg\nfunc (s *Msg) Submit() error {\n\ts.BuildCustomOptions()\n\treturn dtmimp.TransCallDtm(&s.TransBase, \"submit\")\n}\n\n// DoAndSubmitDB short method for Do on db type. please see DoAndSubmit\nfunc (s *Msg) DoAndSubmitDB(queryPrepared string, db *sql.DB, busiCall BarrierBusiFunc) error {\n\treturn s.DoAndSubmit(queryPrepared, func(bb *BranchBarrier) error {\n\t\treturn bb.CallWithDB(db, busiCall)\n\t})\n}\n\n// DoAndSubmit one method for the entire prepare->busi->submit\n// the error returned by busiCall will be returned\n// if busiCall return ErrFailure, then abort is called directly\n// if busiCall return not nil error other than ErrFailure, then DoAndSubmit will call queryPrepared to get the result\nfunc (s *Msg) DoAndSubmit(queryPrepared string, busiCall func(bb *BranchBarrier) error) error {\n\tbb, err := BarrierFrom(s.TransType, s.Gid, dtmimp.MsgDoBranch0, dtmimp.MsgDoOp) // a special barrier for msg QueryPrepared\n\tif err == nil {\n\t\terr = s.Prepare(queryPrepared)\n\t}\n\tif err == nil {\n\t\terrb := busiCall(bb)\n\t\tif errb != nil && !errors.Is(errb, ErrFailure) {\n\t\t\t// if busicall return an error other than failure, we will query the result\n\t\t\t_, err = requestBranch(&s.TransBase, \"GET\", nil, bb.BranchID, bb.Op, queryPrepared)\n\t\t}\n\t\tif errors.Is(errb, ErrFailure) || errors.Is(err, ErrFailure) {\n\t\t\t_ = dtmimp.TransCallDtm(&s.TransBase, \"abort\")\n\t\t} else if err == nil {\n\t\t\terr = s.Submit()\n\t\t}\n\t\tif errb != nil {\n\t\t\treturn errb\n\t\t}\n\t}\n\treturn err\n}\n\n// BuildCustomOptions add custom options to the request context\nfunc (s *Msg) BuildCustomOptions() {\n\tif s.delay > 0 {\n\t\ts.CustomData = dtmimp.MustMarshalString(map[string]interface{}{\"delay\": s.delay})\n\t}\n}\n"
  },
  {
    "path": "client/dtmcli/trans_saga.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmcli\n\nimport (\n\t\"context\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n)\n\n// Saga struct of saga\ntype Saga struct {\n\tdtmimp.TransBase\n\torders map[int][]int\n}\n\n// NewSaga create a saga\nfunc NewSaga(server string, gid string) *Saga {\n\treturn &Saga{TransBase: *dtmimp.NewTransBase(gid, \"saga\", server, \"\"), orders: map[int][]int{}}\n}\n\n// NewSagaWithContext create a saga with context\nfunc NewSagaWithContext(ctx context.Context, server string, gid string) *Saga {\n\tsaga := NewSaga(server, gid)\n\tsaga.TransBase.Context = ctx\n\treturn saga\n}\n\n// Add add a saga step\nfunc (s *Saga) Add(action string, compensate string, postData interface{}) *Saga {\n\ts.Steps = append(s.Steps, map[string]string{\"action\": action, \"compensate\": compensate})\n\ts.Payloads = append(s.Payloads, dtmimp.MustMarshalString(postData))\n\treturn s\n}\n\n// AddBranchOrder specify that branch should be after preBranches. branch should is larger than all the element in preBranches\nfunc (s *Saga) AddBranchOrder(branch int, preBranches []int) *Saga {\n\ts.orders[branch] = preBranches\n\treturn s\n}\n\n// SetConcurrent enable the concurrent exec of sub trans\nfunc (s *Saga) SetConcurrent() *Saga {\n\ts.Concurrent = true\n\treturn s\n}\n\n// Submit submit the saga trans\nfunc (s *Saga) Submit() error {\n\ts.BuildCustomOptions()\n\treturn dtmimp.TransCallDtm(&s.TransBase, \"submit\")\n}\n\n// BuildCustomOptions add custom options to the request context\nfunc (s *Saga) BuildCustomOptions() {\n\tif s.Concurrent {\n\t\ts.CustomData = dtmimp.MustMarshalString(map[string]interface{}{\"orders\": s.orders, \"concurrent\": s.Concurrent})\n\t}\n}\n"
  },
  {
    "path": "client/dtmcli/trans_tcc.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmcli\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/go-resty/resty/v2\"\n)\n\n// Tcc struct of tcc\ntype Tcc struct {\n\tdtmimp.TransBase\n}\n\n// TccGlobalFunc type of global tcc call\ntype TccGlobalFunc func(tcc *Tcc) (*resty.Response, error)\n\n// TccGlobalTransaction begin a tcc global transaction\n// dtm dtm server address\n// gid global transaction ID\n// tccFunc define the detail tcc busi\nfunc TccGlobalTransaction(dtm string, gid string, tccFunc TccGlobalFunc) (rerr error) {\n\treturn TccGlobalTransaction2(dtm, gid, func(t *Tcc) {}, tccFunc)\n}\n\n// TccGlobalTransaction2 new version of TccGlobalTransaction, add custom param\nfunc TccGlobalTransaction2(dtm string, gid string, custom func(*Tcc), tccFunc TccGlobalFunc) (rerr error) {\n\ttcc := &Tcc{TransBase: *dtmimp.NewTransBase(gid, \"tcc\", dtm, \"\")}\n\tcustom(tcc)\n\trerr = dtmimp.TransCallDtm(&tcc.TransBase, \"prepare\")\n\tif rerr != nil {\n\t\treturn rerr\n\t}\n\tdefer dtmimp.DeferDo(&rerr, func() error {\n\t\treturn dtmimp.TransCallDtm(&tcc.TransBase, \"submit\")\n\t}, func() error {\n\t\tif rerr != nil {\n\t\t\ttcc.RollbackReason = rerr.Error()\n\t\t}\n\t\treturn dtmimp.TransCallDtm(&tcc.TransBase, \"abort\")\n\t})\n\t_, rerr = tccFunc(tcc)\n\treturn\n}\n\n// TccFromQuery tcc from request info\nfunc TccFromQuery(qs url.Values) (*Tcc, error) {\n\ttcc := &Tcc{TransBase: *dtmimp.TransBaseFromQuery(qs)}\n\tif tcc.Dtm == \"\" || tcc.Gid == \"\" {\n\t\treturn nil, fmt.Errorf(\"bad tcc info. dtm: %s, gid: %s parentID: %s\", tcc.Dtm, tcc.Gid, tcc.BranchID)\n\t}\n\treturn tcc, nil\n}\n\n// CallBranch call a tcc branch\nfunc (t *Tcc) CallBranch(body interface{}, tryURL string, confirmURL string, cancelURL string) (*resty.Response, error) {\n\tbranchID := t.NewSubBranchID()\n\terr := dtmimp.TransRegisterBranch(&t.TransBase, map[string]string{\n\t\t\"data\":           dtmimp.MustMarshalString(body),\n\t\t\"branch_id\":      branchID,\n\t\tdtmimp.OpConfirm: confirmURL,\n\t\tdtmimp.OpCancel:  cancelURL,\n\t}, \"registerBranch\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn requestBranch(&t.TransBase, \"POST\", body, branchID, dtmimp.OpTry, tryURL)\n}\n"
  },
  {
    "path": "client/dtmcli/types.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmcli\n\nimport (\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/go-resty/resty/v2\"\n)\n\n// DB interface\ntype DB = dtmimp.DB\n\n// TransOptions transaction option\ntype TransOptions = dtmimp.TransOptions\n\n// DBConf declares db configuration\ntype DBConf = dtmimp.DBConf\n\n// SetCurrentDBType set currentDBType\nfunc SetCurrentDBType(dbType string) {\n\tdtmimp.SetCurrentDBType(dbType)\n}\n\n// GetCurrentDBType get currentDBType\nfunc GetCurrentDBType() string {\n\treturn dtmimp.GetCurrentDBType()\n}\n\n// SetBarrierTableName sets barrier table name\nfunc SetBarrierTableName(tablename string) {\n\tdtmimp.BarrierTableName = tablename\n}\n\n// GetRestyClient get the resty.Client for http request\nfunc GetRestyClient() *resty.Client {\n\treturn dtmimp.GetRestyClient2(0)\n}\n\n// GetRestyClient2 get the resty.Client with the specified timeout set\nfunc GetRestyClient2(timeout time.Duration) *resty.Client {\n\treturn dtmimp.GetRestyClient2(timeout)\n}\n"
  },
  {
    "path": "client/dtmcli/types_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmcli\n\nimport (\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestTypes(t *testing.T) {\n\terr := dtmimp.CatchP(func() {\n\t\tMustGenGid(\"http://localhost:36789/api/no\")\n\t})\n\tassert.Error(t, err)\n\tassert.Error(t, err)\n\t_, err = BarrierFromQuery(url.Values{})\n\tassert.Error(t, err)\n\n}\n\nfunc TestXaSqlTimeout(t *testing.T) {\n\tSetBarrierTableName(dtmimp.BarrierTableName) // just cover this func\n}\n"
  },
  {
    "path": "client/dtmcli/utils.go",
    "content": "package dtmcli\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/go-resty/resty/v2\"\n)\n\n// MustGenGid generate a new gid\nfunc MustGenGid(server string) string {\n\tres := map[string]string{}\n\tresp, err := GetRestyClient().R().SetResult(&res).Get(server + \"/newGid\")\n\tif err != nil || res[\"gid\"] == \"\" {\n\t\tpanic(fmt.Errorf(\"newGid error: %v, resp: %s\", err, resp))\n\t}\n\treturn res[\"gid\"]\n}\n\n// ErrorMessage2Error return an error fmt.Errorf(\"%s %w\", errMsg, err) but trim out duplicate wrap\n// eg. ErrorMessage2Error(\"an error. FAILURE\", ErrFailure) return an error with message: \"an error. FAILURE\",\n// no additional \" FAILURE\" added\nfunc ErrorMessage2Error(errMsg string, err error) error {\n\terrMsg = strings.TrimSuffix(errMsg, \" \"+err.Error())\n\treturn fmt.Errorf(\"%s %w\", errMsg, err)\n}\n\n// HTTPResp2DtmError translate a resty response to error\n// compatible with version < v1.10\nfunc HTTPResp2DtmError(resp *resty.Response) error {\n\tcode := resp.StatusCode()\n\tstr := resp.String()\n\tif code == http.StatusTooEarly || strings.Contains(str, ResultOngoing) {\n\t\treturn ErrorMessage2Error(str, ErrOngoing)\n\t} else if code == http.StatusConflict || strings.Contains(str, ResultFailure) {\n\t\treturn ErrorMessage2Error(str, ErrFailure)\n\t} else if code != http.StatusOK {\n\t\treturn errors.New(str)\n\t}\n\treturn nil\n}\n\n// Result2HttpJSON return the http code and json result\n// if result is error, the return proper code, else return StatusOK\nfunc Result2HttpJSON(result interface{}) (code int, res interface{}) {\n\terr, _ := result.(error)\n\tif err == nil {\n\t\tcode = http.StatusOK\n\t\tres = result\n\t} else {\n\t\tres = map[string]string{\n\t\t\t\"error\": err.Error(),\n\t\t}\n\t\tif errors.Is(err, ErrFailure) {\n\t\t\tcode = http.StatusConflict\n\t\t} else if errors.Is(err, ErrOngoing) {\n\t\t\tcode = http.StatusTooEarly\n\t\t} else if err != nil {\n\t\t\tcode = http.StatusInternalServerError\n\t\t}\n\t}\n\treturn\n}\n\nfunc requestBranch(t *dtmimp.TransBase, method string, body interface{}, branchID string, op string, url string) (*resty.Response, error) {\n\tresp, err := dtmimp.TransRequestBranch(t, method, body, branchID, op, url)\n\tif err == nil {\n\t\terr = HTTPResp2DtmError(resp)\n\t}\n\treturn resp, err\n}\n"
  },
  {
    "path": "client/dtmcli/xa.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmcli\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"net/url\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/go-resty/resty/v2\"\n)\n\n// XaGlobalFunc type of xa global function\ntype XaGlobalFunc func(xa *Xa) (*resty.Response, error)\n\n// XaLocalFunc type of xa local function\ntype XaLocalFunc func(db *sql.DB, xa *Xa) error\n\n// Xa xa transaction\ntype Xa struct {\n\tdtmimp.TransBase\n\tPhase2URL string\n}\n\n// XaFromQuery construct xa info from request\nfunc XaFromQuery(qs url.Values) (*Xa, error) {\n\txa := &Xa{TransBase: *dtmimp.TransBaseFromQuery(qs)}\n\txa.Op = dtmimp.EscapeGet(qs, \"op\")\n\txa.Phase2URL = dtmimp.EscapeGet(qs, \"phase2_url\")\n\tif xa.Gid == \"\" || xa.BranchID == \"\" || xa.Op == \"\" {\n\t\treturn nil, fmt.Errorf(\"bad xa info: gid: %s branchid: %s op: %s phase2_url: %s\", xa.Gid, xa.BranchID, xa.Op, xa.Phase2URL)\n\t}\n\treturn xa, nil\n}\n\n// XaLocalTransaction start a xa local transaction\nfunc XaLocalTransaction(qs url.Values, dbConf DBConf, xaFunc XaLocalFunc) error {\n\txa, err := XaFromQuery(qs)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif xa.Op == dtmimp.OpCommit || xa.Op == dtmimp.OpRollback {\n\t\treturn dtmimp.XaHandlePhase2(xa.Gid, dbConf, xa.BranchID, xa.Op)\n\t}\n\treturn dtmimp.XaHandleLocalTrans(&xa.TransBase, dbConf, func(db *sql.DB) error {\n\t\terr := xaFunc(db, xa)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn dtmimp.TransRegisterBranch(&xa.TransBase, map[string]string{\n\t\t\t\"url\":       xa.Phase2URL,\n\t\t\t\"branch_id\": xa.BranchID,\n\t\t}, \"registerBranch\")\n\t})\n}\n\n// XaGlobalTransaction start a xa global transaction\nfunc XaGlobalTransaction(server string, gid string, xaFunc XaGlobalFunc) error {\n\treturn XaGlobalTransaction2(server, gid, func(x *Xa) {}, xaFunc)\n}\n\n// XaGlobalTransaction2 start a xa global transaction with xa custom function\nfunc XaGlobalTransaction2(server string, gid string, custom func(*Xa), xaFunc XaGlobalFunc) (rerr error) {\n\txa := &Xa{TransBase: *dtmimp.NewTransBase(gid, \"xa\", server, \"\")}\n\tcustom(xa)\n\treturn dtmimp.XaHandleGlobalTrans(&xa.TransBase, func(action string) error {\n\t\treturn dtmimp.TransCallDtm(&xa.TransBase, action)\n\t}, func() error {\n\t\t_, rerr := xaFunc(xa)\n\t\treturn rerr\n\t})\n}\n\n// CallBranch call a xa branch\nfunc (x *Xa) CallBranch(body interface{}, url string) (*resty.Response, error) {\n\tbranchID := x.NewSubBranchID()\n\treturn requestBranch(&x.TransBase, \"POST\", body, branchID, dtmimp.OpAction, url)\n}\n"
  },
  {
    "path": "client/dtmgrpc/barrier.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmgrpc\n\nimport (\n\t\"context\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n)\n\n// BarrierFromGrpc generate a Barrier from grpc context\nfunc BarrierFromGrpc(ctx context.Context) (*dtmcli.BranchBarrier, error) {\n\ttb := dtmgimp.TransBaseFromGrpc(ctx)\n\treturn dtmcli.BarrierFrom(tb.TransType, tb.Gid, tb.BranchID, tb.Op)\n}\n"
  },
  {
    "path": "client/dtmgrpc/dtmgimp/README-cn.md",
    "content": "## 注意\n此包带imp后缀，主要被dtm内部使用，相关接口可能会发生变更，请勿使用这里的接口"
  },
  {
    "path": "client/dtmgrpc/dtmgimp/README.md",
    "content": "## Notice\nPlease donot use this package, and this package should only be used in dtm internally. The interfaces are not stable, and package name has postfix \"imp\""
  },
  {
    "path": "client/dtmgrpc/dtmgimp/grpc_clients.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmgimp\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"sync\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgpb\"\n\t\"github.com/dtm-labs/dtmdriver\"\n\t\"github.com/dtm-labs/logger\"\n\tgrpc \"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/credentials/insecure\"\n)\n\ntype rawCodec struct{}\n\nfunc (cb rawCodec) Marshal(v interface{}) ([]byte, error) {\n\treturn v.([]byte), nil\n}\n\nfunc (cb rawCodec) Unmarshal(data []byte, v interface{}) error {\n\tba, ok := v.(*[]byte)\n\tdtmimp.PanicIf(!ok, fmt.Errorf(\"please pass in *[]byte\"))\n\t*ba = append(*ba, data...)\n\n\treturn nil\n}\n\nfunc (cb rawCodec) Name() string { return \"dtm_raw\" }\n\nvar normalClients, rawClients sync.Map\n\n// ClientInterceptors declares grpc.UnaryClientInterceptors slice\nvar ClientInterceptors = []grpc.UnaryClientInterceptor{}\n\n// GrpcServiceConfigGetter is a function to get gRPC service config, can be set by server config\nvar GrpcServiceConfigGetter func() string\n\n// MustGetDtmClient 1\nfunc MustGetDtmClient(grpcServer string) dtmgpb.DtmClient {\n\treturn dtmgpb.NewDtmClient(MustGetGrpcConn(grpcServer, false))\n}\n\n// GetGrpcConn 1\nfunc GetGrpcConn(grpcServer string, isRaw bool) (conn *grpc.ClientConn, rerr error) {\n\tclients := &normalClients\n\tif isRaw {\n\t\tclients = &rawClients\n\t}\n\tgrpcServer = dtmimp.MayReplaceLocalhost(grpcServer)\n\tv, ok := clients.Load(grpcServer)\n\tif !ok {\n\t\topts := grpc.WithDefaultCallOptions()\n\t\tif isRaw {\n\t\t\topts = grpc.WithDefaultCallOptions(grpc.ForceCodec(rawCodec{}))\n\t\t}\n\t\tlogger.Debugf(\"grpc client connecting %s\", grpcServer)\n\t\tinterceptors := append(ClientInterceptors, GrpcClientLog)\n\t\tinterceptors = append(interceptors, dtmdriver.Middlewares.Grpc...)\n\t\tinOpt := grpc.WithChainUnaryInterceptor(interceptors...)\n\t\tgrpcServiceConfig, hasConfig := getGrpcServiceConfig()\n\t\tdialOpts := []grpc.DialOption{inOpt, grpc.WithTransportCredentials(insecure.NewCredentials()), opts}\n\t\tif hasConfig {\n\t\t\tdialOpts = append(dialOpts, grpc.WithDefaultServiceConfig(grpcServiceConfig))\n\t\t}\n\t\tconn, rerr := grpc.Dial(grpcServer, dialOpts...)\n\t\tif rerr == nil {\n\t\t\tclients.Store(grpcServer, conn)\n\t\t\tv = conn\n\t\t\tlogger.Debugf(\"grpc client inited for %s\", grpcServer)\n\t\t}\n\t}\n\treturn v.(*grpc.ClientConn), rerr\n}\n\n// MustGetGrpcConn 1\nfunc MustGetGrpcConn(grpcServer string, isRaw bool) *grpc.ClientConn {\n\tconn, err := GetGrpcConn(grpcServer, isRaw)\n\tdtmimp.E2P(err)\n\treturn conn\n}\n\n// getGrpcServiceConfig returns the gRPC service config from config getter or environment variable, and a bool indicating if config is set\nfunc getGrpcServiceConfig() (string, bool) {\n\t// First try to get from config getter (set by server)\n\tif GrpcServiceConfigGetter != nil {\n\t\tif config := GrpcServiceConfigGetter(); config != \"\" {\n\t\t\treturn config, true\n\t\t}\n\t}\n\t// Fallback to environment variable\n\tconfig := os.Getenv(\"GRPC_SERVICE_CONFIG\")\n\tif config != \"\" {\n\t\treturn config, true\n\t}\n\t// No config set\n\treturn \"\", false\n}\n"
  },
  {
    "path": "client/dtmgrpc/dtmgimp/types.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmgimp\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtmdriver\"\n\t\"github.com/dtm-labs/logger\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/metadata\"\n\t\"google.golang.org/grpc/status\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\n// GrpcServerLog middleware to print server-side grpc log\nfunc GrpcServerLog(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {\n\tbegan := time.Now()\n\tlogger.Debugf(\"grpc server handling: %s %s\", info.FullMethod, dtmimp.MustMarshalString(req))\n\tLogDtmCtx(ctx)\n\tm, err := handler(ctx, req)\n\tres := fmt.Sprintf(\"%2dms %v %s %s %s\",\n\t\ttime.Since(began).Milliseconds(), err, info.FullMethod, dtmimp.MustMarshalString(m), dtmimp.MustMarshalString(req))\n\tst, _ := status.FromError(err)\n\tif err == nil || st != nil && st.Code() == codes.FailedPrecondition {\n\t\tlogger.Infof(\"%s\", res)\n\t} else {\n\t\tlogger.Errorf(\"%s\", res)\n\t}\n\treturn m, err\n}\n\n// GrpcClientLog middleware to print client-side grpc log\nfunc GrpcClientLog(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {\n\tlogger.Debugf(\"grpc client calling: %s%s %v\", cc.Target(), method, dtmimp.MustMarshalString(req))\n\tLogDtmCtx(ctx)\n\terr := invoker(ctx, method, req, reply, cc, opts...)\n\tres := fmt.Sprintf(\"grpc client called: %s%s %s result: %s err: %v\",\n\t\tcc.Target(), method, dtmimp.MustMarshalString(req), dtmimp.MustMarshalString(reply), err)\n\tst, _ := status.FromError(err)\n\tif err == nil || st != nil && st.Code() == codes.FailedPrecondition {\n\t\tlogger.Infof(\"%s\", res)\n\t} else {\n\t\tlogger.Errorf(\"%s\", res)\n\t}\n\treturn err\n}\n\n// InvokeBranch invoke a url for trans\nfunc InvokeBranch(t *dtmimp.TransBase, isRaw bool, msg proto.Message, url string, reply interface{}, branchID string, op string, opts ...grpc.CallOption) error {\n\tserver, method, err := dtmdriver.GetDriver().ParseServerMethod(url)\n\tif err != nil {\n\t\treturn err\n\t}\n\tctx := TransInfo2Ctx(t.Context, t.Gid, t.TransType, branchID, op, t.Dtm)\n\tctx = metadata.AppendToOutgoingContext(ctx, Map2Kvs(t.BranchHeaders)...)\n\tif t.TransType == \"xa\" { // xa branch need additional phase2_url\n\t\tctx = metadata.AppendToOutgoingContext(ctx, Map2Kvs(map[string]string{dtmpre + \"phase2_url\": url})...)\n\t}\n\treturn MustGetGrpcConn(server, isRaw).Invoke(ctx, method, msg, reply, opts...)\n}\n"
  },
  {
    "path": "client/dtmgrpc/dtmgimp/utils.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmgimp\n\nimport (\n\t\"context\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgpb\"\n\t\"github.com/dtm-labs/logger\"\n\t\"google.golang.org/grpc/metadata\"\n\t\"google.golang.org/protobuf/proto\"\n\t\"google.golang.org/protobuf/types/known/emptypb\"\n)\n\n// MustProtoMarshal must version of proto.Marshal\nfunc MustProtoMarshal(msg proto.Message) []byte {\n\tb, err := proto.Marshal(msg)\n\tdtmimp.PanicIf(err != nil, err)\n\treturn b\n}\n\n// MustProtoUnmarshal must version of proto.Unmarshal\nfunc MustProtoUnmarshal(data []byte, msg proto.Message) {\n\terr := proto.Unmarshal(data, msg)\n\tdtmimp.PanicIf(err != nil, err)\n}\n\n// GetDtmRequest return a DtmRequest from TransBase\nfunc GetDtmRequest(s *dtmimp.TransBase) *dtmgpb.DtmRequest {\n\treturn &dtmgpb.DtmRequest{\n\t\tGid:       s.Gid,\n\t\tTransType: s.TransType,\n\t\tTransOptions: &dtmgpb.DtmTransOptions{\n\t\t\tWaitResult:     s.WaitResult,\n\t\t\tTimeoutToFail:  s.TimeoutToFail,\n\t\t\tRetryInterval:  s.RetryInterval,\n\t\t\tBranchHeaders:  s.BranchHeaders,\n\t\t\tRequestTimeout: s.RequestTimeout,\n\t\t\tRetryLimit:     s.RetryLimit,\n\t\t},\n\t\tQueryPrepared:  s.QueryPrepared,\n\t\tCustomedData:   s.CustomData,\n\t\tBinPayloads:    s.BinPayloads,\n\t\tSteps:          dtmimp.MustMarshalString(s.Steps),\n\t\tRollbackReason: s.RollbackReason,\n\t}\n}\n\n// DtmGrpcCall make a convenient call to dtm\nfunc DtmGrpcCall(s *dtmimp.TransBase, operation string) error {\n\treply := emptypb.Empty{}\n\treturn MustGetGrpcConn(s.Dtm, false).Invoke(s.Context, \"/dtmgimp.Dtm/\"+operation, GetDtmRequest(s), &reply)\n}\n\nconst dtmpre string = \"dtm-\"\n\n// TransInfo2Ctx add trans info to grpc context\nfunc TransInfo2Ctx(ctx context.Context, gid, transType, branchID, op, dtm string) context.Context {\n\tnctx := ctx\n\tif ctx == nil {\n\t\tnctx = context.Background()\n\t}\n\treturn metadata.AppendToOutgoingContext(\n\t\tnctx,\n\t\tdtmpre+\"gid\", gid,\n\t\tdtmpre+\"trans_type\", transType,\n\t\tdtmpre+\"branch_id\", branchID,\n\t\tdtmpre+\"op\", op,\n\t\tdtmpre+\"dtm\", dtm,\n\t)\n}\n\n// Map2Kvs map to metadata kv\nfunc Map2Kvs(m map[string]string) []string {\n\tkvs := make([]string, 0, len(m)*2)\n\tfor k, v := range m {\n\t\tkvs = append(kvs, k, v)\n\t}\n\treturn kvs\n}\n\n// LogDtmCtx logout dtm info in context metadata\nfunc LogDtmCtx(ctx context.Context) {\n\ttb := TransBaseFromGrpc(ctx)\n\tif tb.Gid != \"\" {\n\t\tlogger.Debugf(\"gid: %s trans_type: %s branch_id: %s op: %s dtm: %s\", tb.Gid, tb.TransType, tb.BranchID, tb.Op, tb.Dtm)\n\t}\n}\n\nfunc dtmGet(md metadata.MD, key string) string {\n\treturn mdGet(md, dtmpre+key)\n}\n\nfunc mdGet(md metadata.MD, key string) string {\n\tv := md.Get(key)\n\tif len(v) == 0 {\n\t\treturn \"\"\n\t}\n\treturn v[0]\n}\n\n// TransBaseFromGrpc get trans base info from a context metadata\nfunc TransBaseFromGrpc(ctx context.Context) *dtmimp.TransBase {\n\tmd, _ := metadata.FromIncomingContext(ctx)\n\ttb := dtmimp.NewTransBase(dtmGet(md, \"gid\"), dtmGet(md, \"trans_type\"), dtmGet(md, \"dtm\"), dtmGet(md, \"branch_id\"))\n\ttb.Op = dtmGet(md, \"op\")\n\treturn tb\n}\n\n// GetMetaFromContext get header from context\nfunc GetMetaFromContext(ctx context.Context, name string) string {\n\tmd, _ := metadata.FromIncomingContext(ctx)\n\treturn mdGet(md, name)\n}\n\n// GetDtmMetaFromContext get dtm header from context\nfunc GetDtmMetaFromContext(ctx context.Context, name string) string {\n\tmd, _ := metadata.FromIncomingContext(ctx)\n\treturn dtmGet(md, name)\n}\n\ntype requestTimeoutKey struct{}\n\n// RequestTimeoutFromContext returns requestTime of transOption option\nfunc RequestTimeoutFromContext(ctx context.Context) int64 {\n\tif v, ok := ctx.Value(requestTimeoutKey{}).(int64); ok {\n\t\treturn v\n\t}\n\n\treturn 0\n}\n\n// RequestTimeoutNewContext sets requestTimeout of transOption option to context\nfunc RequestTimeoutNewContext(ctx context.Context, requestTimeout int64) context.Context {\n\treturn context.WithValue(ctx, requestTimeoutKey{}, requestTimeout)\n}\n"
  },
  {
    "path": "client/dtmgrpc/dtmgpb/dtmgimp.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.28.1\n// \tprotoc        v3.21.12\n// source: dtmgimp.proto\n\npackage dtmgpb\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\temptypb \"google.golang.org/protobuf/types/known/emptypb\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype DtmTransOptions struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tWaitResult    bool  `protobuf:\"varint,1,opt,name=WaitResult,proto3\" json:\"WaitResult,omitempty\"`\n\tTimeoutToFail int64 `protobuf:\"varint,2,opt,name=TimeoutToFail,proto3\" json:\"TimeoutToFail,omitempty\"`\n\tRetryInterval int64 `protobuf:\"varint,3,opt,name=RetryInterval,proto3\" json:\"RetryInterval,omitempty\"`\n\t// repeated string PassthroughHeaders = 4; // depreceated\n\tBranchHeaders  map[string]string `protobuf:\"bytes,5,rep,name=BranchHeaders,proto3\" json:\"BranchHeaders,omitempty\" protobuf_key:\"bytes,1,opt,name=key,proto3\" protobuf_val:\"bytes,2,opt,name=value,proto3\"`\n\tRequestTimeout int64             `protobuf:\"varint,6,opt,name=RequestTimeout,proto3\" json:\"RequestTimeout,omitempty\"`\n\tRetryLimit     int64             `protobuf:\"varint,7,opt,name=RetryLimit,proto3\" json:\"RetryLimit,omitempty\"`\n}\n\nfunc (x *DtmTransOptions) Reset() {\n\t*x = DtmTransOptions{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_dtmgimp_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DtmTransOptions) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DtmTransOptions) ProtoMessage() {}\n\nfunc (x *DtmTransOptions) ProtoReflect() protoreflect.Message {\n\tmi := &file_dtmgimp_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DtmTransOptions.ProtoReflect.Descriptor instead.\nfunc (*DtmTransOptions) Descriptor() ([]byte, []int) {\n\treturn file_dtmgimp_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *DtmTransOptions) GetWaitResult() bool {\n\tif x != nil {\n\t\treturn x.WaitResult\n\t}\n\treturn false\n}\n\nfunc (x *DtmTransOptions) GetTimeoutToFail() int64 {\n\tif x != nil {\n\t\treturn x.TimeoutToFail\n\t}\n\treturn 0\n}\n\nfunc (x *DtmTransOptions) GetRetryInterval() int64 {\n\tif x != nil {\n\t\treturn x.RetryInterval\n\t}\n\treturn 0\n}\n\nfunc (x *DtmTransOptions) GetBranchHeaders() map[string]string {\n\tif x != nil {\n\t\treturn x.BranchHeaders\n\t}\n\treturn nil\n}\n\nfunc (x *DtmTransOptions) GetRequestTimeout() int64 {\n\tif x != nil {\n\t\treturn x.RequestTimeout\n\t}\n\treturn 0\n}\n\nfunc (x *DtmTransOptions) GetRetryLimit() int64 {\n\tif x != nil {\n\t\treturn x.RetryLimit\n\t}\n\treturn 0\n}\n\n// DtmRequest request sent to dtm server\ntype DtmRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tGid            string            `protobuf:\"bytes,1,opt,name=Gid,proto3\" json:\"Gid,omitempty\"`\n\tTransType      string            `protobuf:\"bytes,2,opt,name=TransType,proto3\" json:\"TransType,omitempty\"`\n\tTransOptions   *DtmTransOptions  `protobuf:\"bytes,3,opt,name=TransOptions,proto3\" json:\"TransOptions,omitempty\"`\n\tCustomedData   string            `protobuf:\"bytes,4,opt,name=CustomedData,proto3\" json:\"CustomedData,omitempty\"`\n\tBinPayloads    [][]byte          `protobuf:\"bytes,5,rep,name=BinPayloads,proto3\" json:\"BinPayloads,omitempty\"`     // for Msg/Saga/Workflow branch payloads\n\tQueryPrepared  string            `protobuf:\"bytes,6,opt,name=QueryPrepared,proto3\" json:\"QueryPrepared,omitempty\"` // for Msg\n\tSteps          string            `protobuf:\"bytes,7,opt,name=Steps,proto3\" json:\"Steps,omitempty\"`\n\tReqExtra       map[string]string `protobuf:\"bytes,8,rep,name=ReqExtra,proto3\" json:\"ReqExtra,omitempty\" protobuf_key:\"bytes,1,opt,name=key,proto3\" protobuf_val:\"bytes,2,opt,name=value,proto3\"`\n\tRollbackReason string            `protobuf:\"bytes,9,opt,name=RollbackReason,proto3\" json:\"RollbackReason,omitempty\"`\n}\n\nfunc (x *DtmRequest) Reset() {\n\t*x = DtmRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_dtmgimp_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DtmRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DtmRequest) ProtoMessage() {}\n\nfunc (x *DtmRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_dtmgimp_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DtmRequest.ProtoReflect.Descriptor instead.\nfunc (*DtmRequest) Descriptor() ([]byte, []int) {\n\treturn file_dtmgimp_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *DtmRequest) GetGid() string {\n\tif x != nil {\n\t\treturn x.Gid\n\t}\n\treturn \"\"\n}\n\nfunc (x *DtmRequest) GetTransType() string {\n\tif x != nil {\n\t\treturn x.TransType\n\t}\n\treturn \"\"\n}\n\nfunc (x *DtmRequest) GetTransOptions() *DtmTransOptions {\n\tif x != nil {\n\t\treturn x.TransOptions\n\t}\n\treturn nil\n}\n\nfunc (x *DtmRequest) GetCustomedData() string {\n\tif x != nil {\n\t\treturn x.CustomedData\n\t}\n\treturn \"\"\n}\n\nfunc (x *DtmRequest) GetBinPayloads() [][]byte {\n\tif x != nil {\n\t\treturn x.BinPayloads\n\t}\n\treturn nil\n}\n\nfunc (x *DtmRequest) GetQueryPrepared() string {\n\tif x != nil {\n\t\treturn x.QueryPrepared\n\t}\n\treturn \"\"\n}\n\nfunc (x *DtmRequest) GetSteps() string {\n\tif x != nil {\n\t\treturn x.Steps\n\t}\n\treturn \"\"\n}\n\nfunc (x *DtmRequest) GetReqExtra() map[string]string {\n\tif x != nil {\n\t\treturn x.ReqExtra\n\t}\n\treturn nil\n}\n\nfunc (x *DtmRequest) GetRollbackReason() string {\n\tif x != nil {\n\t\treturn x.RollbackReason\n\t}\n\treturn \"\"\n}\n\ntype DtmGidReply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tGid string `protobuf:\"bytes,1,opt,name=Gid,proto3\" json:\"Gid,omitempty\"`\n}\n\nfunc (x *DtmGidReply) Reset() {\n\t*x = DtmGidReply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_dtmgimp_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DtmGidReply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DtmGidReply) ProtoMessage() {}\n\nfunc (x *DtmGidReply) ProtoReflect() protoreflect.Message {\n\tmi := &file_dtmgimp_proto_msgTypes[2]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DtmGidReply.ProtoReflect.Descriptor instead.\nfunc (*DtmGidReply) Descriptor() ([]byte, []int) {\n\treturn file_dtmgimp_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *DtmGidReply) GetGid() string {\n\tif x != nil {\n\t\treturn x.Gid\n\t}\n\treturn \"\"\n}\n\ntype DtmBranchRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tGid         string            `protobuf:\"bytes,1,opt,name=Gid,proto3\" json:\"Gid,omitempty\"`\n\tTransType   string            `protobuf:\"bytes,2,opt,name=TransType,proto3\" json:\"TransType,omitempty\"`\n\tBranchID    string            `protobuf:\"bytes,3,opt,name=BranchID,proto3\" json:\"BranchID,omitempty\"`\n\tOp          string            `protobuf:\"bytes,4,opt,name=Op,proto3\" json:\"Op,omitempty\"`\n\tData        map[string]string `protobuf:\"bytes,5,rep,name=Data,proto3\" json:\"Data,omitempty\" protobuf_key:\"bytes,1,opt,name=key,proto3\" protobuf_val:\"bytes,2,opt,name=value,proto3\"`\n\tBusiPayload []byte            `protobuf:\"bytes,6,opt,name=BusiPayload,proto3\" json:\"BusiPayload,omitempty\"`\n}\n\nfunc (x *DtmBranchRequest) Reset() {\n\t*x = DtmBranchRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_dtmgimp_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DtmBranchRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DtmBranchRequest) ProtoMessage() {}\n\nfunc (x *DtmBranchRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_dtmgimp_proto_msgTypes[3]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DtmBranchRequest.ProtoReflect.Descriptor instead.\nfunc (*DtmBranchRequest) Descriptor() ([]byte, []int) {\n\treturn file_dtmgimp_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (x *DtmBranchRequest) GetGid() string {\n\tif x != nil {\n\t\treturn x.Gid\n\t}\n\treturn \"\"\n}\n\nfunc (x *DtmBranchRequest) GetTransType() string {\n\tif x != nil {\n\t\treturn x.TransType\n\t}\n\treturn \"\"\n}\n\nfunc (x *DtmBranchRequest) GetBranchID() string {\n\tif x != nil {\n\t\treturn x.BranchID\n\t}\n\treturn \"\"\n}\n\nfunc (x *DtmBranchRequest) GetOp() string {\n\tif x != nil {\n\t\treturn x.Op\n\t}\n\treturn \"\"\n}\n\nfunc (x *DtmBranchRequest) GetData() map[string]string {\n\tif x != nil {\n\t\treturn x.Data\n\t}\n\treturn nil\n}\n\nfunc (x *DtmBranchRequest) GetBusiPayload() []byte {\n\tif x != nil {\n\t\treturn x.BusiPayload\n\t}\n\treturn nil\n}\n\ntype DtmProgressesReply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tTransaction *DtmTransaction `protobuf:\"bytes,1,opt,name=Transaction,json=transaction,proto3\" json:\"Transaction,omitempty\"`\n\tProgresses  []*DtmProgress  `protobuf:\"bytes,2,rep,name=Progresses,json=progresses,proto3\" json:\"Progresses,omitempty\"`\n}\n\nfunc (x *DtmProgressesReply) Reset() {\n\t*x = DtmProgressesReply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_dtmgimp_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DtmProgressesReply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DtmProgressesReply) ProtoMessage() {}\n\nfunc (x *DtmProgressesReply) ProtoReflect() protoreflect.Message {\n\tmi := &file_dtmgimp_proto_msgTypes[4]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DtmProgressesReply.ProtoReflect.Descriptor instead.\nfunc (*DtmProgressesReply) Descriptor() ([]byte, []int) {\n\treturn file_dtmgimp_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *DtmProgressesReply) GetTransaction() *DtmTransaction {\n\tif x != nil {\n\t\treturn x.Transaction\n\t}\n\treturn nil\n}\n\nfunc (x *DtmProgressesReply) GetProgresses() []*DtmProgress {\n\tif x != nil {\n\t\treturn x.Progresses\n\t}\n\treturn nil\n}\n\ntype DtmTransaction struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tGid            string `protobuf:\"bytes,1,opt,name=Gid,json=gid,proto3\" json:\"Gid,omitempty\"`\n\tStatus         string `protobuf:\"bytes,2,opt,name=Status,json=status,proto3\" json:\"Status,omitempty\"`\n\tRollbackReason string `protobuf:\"bytes,3,opt,name=RollbackReason,json=rollback_reason,proto3\" json:\"RollbackReason,omitempty\"`\n\tResult         string `protobuf:\"bytes,4,opt,name=Result,json=result,proto3\" json:\"Result,omitempty\"`\n}\n\nfunc (x *DtmTransaction) Reset() {\n\t*x = DtmTransaction{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_dtmgimp_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DtmTransaction) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DtmTransaction) ProtoMessage() {}\n\nfunc (x *DtmTransaction) ProtoReflect() protoreflect.Message {\n\tmi := &file_dtmgimp_proto_msgTypes[5]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DtmTransaction.ProtoReflect.Descriptor instead.\nfunc (*DtmTransaction) Descriptor() ([]byte, []int) {\n\treturn file_dtmgimp_proto_rawDescGZIP(), []int{5}\n}\n\nfunc (x *DtmTransaction) GetGid() string {\n\tif x != nil {\n\t\treturn x.Gid\n\t}\n\treturn \"\"\n}\n\nfunc (x *DtmTransaction) GetStatus() string {\n\tif x != nil {\n\t\treturn x.Status\n\t}\n\treturn \"\"\n}\n\nfunc (x *DtmTransaction) GetRollbackReason() string {\n\tif x != nil {\n\t\treturn x.RollbackReason\n\t}\n\treturn \"\"\n}\n\nfunc (x *DtmTransaction) GetResult() string {\n\tif x != nil {\n\t\treturn x.Result\n\t}\n\treturn \"\"\n}\n\ntype DtmProgress struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tStatus   string `protobuf:\"bytes,1,opt,name=Status,json=status,proto3\" json:\"Status,omitempty\"`\n\tBinData  []byte `protobuf:\"bytes,2,opt,name=BinData,json=bin_data,proto3\" json:\"BinData,omitempty\"`\n\tBranchID string `protobuf:\"bytes,3,opt,name=BranchID,json=branch_id,proto3\" json:\"BranchID,omitempty\"`\n\tOp       string `protobuf:\"bytes,4,opt,name=Op,json=op,proto3\" json:\"Op,omitempty\"`\n}\n\nfunc (x *DtmProgress) Reset() {\n\t*x = DtmProgress{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_dtmgimp_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DtmProgress) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DtmProgress) ProtoMessage() {}\n\nfunc (x *DtmProgress) ProtoReflect() protoreflect.Message {\n\tmi := &file_dtmgimp_proto_msgTypes[6]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DtmProgress.ProtoReflect.Descriptor instead.\nfunc (*DtmProgress) Descriptor() ([]byte, []int) {\n\treturn file_dtmgimp_proto_rawDescGZIP(), []int{6}\n}\n\nfunc (x *DtmProgress) GetStatus() string {\n\tif x != nil {\n\t\treturn x.Status\n\t}\n\treturn \"\"\n}\n\nfunc (x *DtmProgress) GetBinData() []byte {\n\tif x != nil {\n\t\treturn x.BinData\n\t}\n\treturn nil\n}\n\nfunc (x *DtmProgress) GetBranchID() string {\n\tif x != nil {\n\t\treturn x.BranchID\n\t}\n\treturn \"\"\n}\n\nfunc (x *DtmProgress) GetOp() string {\n\tif x != nil {\n\t\treturn x.Op\n\t}\n\treturn \"\"\n}\n\ntype DtmTopicRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tTopic  string `protobuf:\"bytes,1,opt,name=Topic,proto3\" json:\"Topic,omitempty\"`\n\tURL    string `protobuf:\"bytes,2,opt,name=URL,proto3\" json:\"URL,omitempty\"`\n\tRemark string `protobuf:\"bytes,3,opt,name=Remark,proto3\" json:\"Remark,omitempty\"`\n}\n\nfunc (x *DtmTopicRequest) Reset() {\n\t*x = DtmTopicRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_dtmgimp_proto_msgTypes[7]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *DtmTopicRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*DtmTopicRequest) ProtoMessage() {}\n\nfunc (x *DtmTopicRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_dtmgimp_proto_msgTypes[7]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use DtmTopicRequest.ProtoReflect.Descriptor instead.\nfunc (*DtmTopicRequest) Descriptor() ([]byte, []int) {\n\treturn file_dtmgimp_proto_rawDescGZIP(), []int{7}\n}\n\nfunc (x *DtmTopicRequest) GetTopic() string {\n\tif x != nil {\n\t\treturn x.Topic\n\t}\n\treturn \"\"\n}\n\nfunc (x *DtmTopicRequest) GetURL() string {\n\tif x != nil {\n\t\treturn x.URL\n\t}\n\treturn \"\"\n}\n\nfunc (x *DtmTopicRequest) GetRemark() string {\n\tif x != nil {\n\t\treturn x.Remark\n\t}\n\treturn \"\"\n}\n\nvar File_dtmgimp_proto protoreflect.FileDescriptor\n\nvar file_dtmgimp_proto_rawDesc = []byte{\n\t0x0a, 0x0d, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,\n\t0x07, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,\n\t0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e,\n\t0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xda, 0x02, 0x0a, 0x0f, 0x44, 0x74, 0x6d, 0x54, 0x72, 0x61,\n\t0x6e, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x57, 0x61, 0x69,\n\t0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x57,\n\t0x61, 0x69, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x69, 0x6d,\n\t0x65, 0x6f, 0x75, 0x74, 0x54, 0x6f, 0x46, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03,\n\t0x52, 0x0d, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x54, 0x6f, 0x46, 0x61, 0x69, 0x6c, 0x12,\n\t0x24, 0x0a, 0x0d, 0x52, 0x65, 0x74, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c,\n\t0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x52, 0x65, 0x74, 0x72, 0x79, 0x49, 0x6e, 0x74,\n\t0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x51, 0x0a, 0x0d, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x48,\n\t0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x64,\n\t0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f,\n\t0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x48, 0x65, 0x61,\n\t0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x42, 0x72, 0x61, 0x6e, 0x63,\n\t0x68, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x52, 0x65, 0x71, 0x75,\n\t0x65, 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03,\n\t0x52, 0x0e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,\n\t0x12, 0x1e, 0x0a, 0x0a, 0x52, 0x65, 0x74, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07,\n\t0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x52, 0x65, 0x74, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74,\n\t0x1a, 0x40, 0x0a, 0x12, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72,\n\t0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,\n\t0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,\n\t0x38, 0x01, 0x22, 0xa0, 0x03, 0x0a, 0x0a, 0x44, 0x74, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,\n\t0x74, 0x12, 0x10, 0x0a, 0x03, 0x47, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,\n\t0x47, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x54, 0x79, 0x70, 0x65,\n\t0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x54, 0x79, 0x70,\n\t0x65, 0x12, 0x3c, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,\n\t0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d,\n\t0x70, 0x2e, 0x44, 0x74, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,\n\t0x73, 0x52, 0x0c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12,\n\t0x22, 0x0a, 0x0c, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x18,\n\t0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x64, 0x44,\n\t0x61, 0x74, 0x61, 0x12, 0x20, 0x0a, 0x0b, 0x42, 0x69, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61,\n\t0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0b, 0x42, 0x69, 0x6e, 0x50, 0x61, 0x79,\n\t0x6c, 0x6f, 0x61, 0x64, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72,\n\t0x65, 0x70, 0x61, 0x72, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x51, 0x75,\n\t0x65, 0x72, 0x79, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x53,\n\t0x74, 0x65, 0x70, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x53, 0x74, 0x65, 0x70,\n\t0x73, 0x12, 0x3d, 0x0a, 0x08, 0x52, 0x65, 0x71, 0x45, 0x78, 0x74, 0x72, 0x61, 0x18, 0x08, 0x20,\n\t0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74,\n\t0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x45, 0x78, 0x74, 0x72,\n\t0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x52, 0x65, 0x71, 0x45, 0x78, 0x74, 0x72, 0x61,\n\t0x12, 0x26, 0x0a, 0x0e, 0x52, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x61, 0x73,\n\t0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x52, 0x6f, 0x6c, 0x6c, 0x62, 0x61,\n\t0x63, 0x6b, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x1a, 0x3b, 0x0a, 0x0d, 0x52, 0x65, 0x71, 0x45,\n\t0x78, 0x74, 0x72, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,\n\t0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,\n\t0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x1f, 0x0a, 0x0b, 0x44, 0x74, 0x6d, 0x47, 0x69, 0x64, 0x52,\n\t0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x47, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x09, 0x52, 0x03, 0x47, 0x69, 0x64, 0x22, 0x82, 0x02, 0x0a, 0x10, 0x44, 0x74, 0x6d, 0x42, 0x72,\n\t0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x47,\n\t0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x47, 0x69, 0x64, 0x12, 0x1c, 0x0a,\n\t0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,\n\t0x52, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x42,\n\t0x72, 0x61, 0x6e, 0x63, 0x68, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x42,\n\t0x72, 0x61, 0x6e, 0x63, 0x68, 0x49, 0x44, 0x12, 0x0e, 0x0a, 0x02, 0x4f, 0x70, 0x18, 0x04, 0x20,\n\t0x01, 0x28, 0x09, 0x52, 0x02, 0x4f, 0x70, 0x12, 0x37, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18,\n\t0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e,\n\t0x44, 0x74, 0x6d, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,\n\t0x2e, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61,\n\t0x12, 0x20, 0x0a, 0x0b, 0x42, 0x75, 0x73, 0x69, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18,\n\t0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x42, 0x75, 0x73, 0x69, 0x50, 0x61, 0x79, 0x6c, 0x6f,\n\t0x61, 0x64, 0x1a, 0x37, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,\n\t0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,\n\t0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,\n\t0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x85, 0x01, 0x0a, 0x12,\n\t0x44, 0x74, 0x6d, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x70,\n\t0x6c, 0x79, 0x12, 0x39, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f,\n\t0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d,\n\t0x70, 0x2e, 0x44, 0x74, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,\n\t0x52, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a,\n\t0x0a, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,\n\t0x0b, 0x32, 0x14, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x50,\n\t0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73,\n\t0x73, 0x65, 0x73, 0x22, 0x7b, 0x0a, 0x0e, 0x44, 0x74, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61,\n\t0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x47, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x03, 0x67, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75,\n\t0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12,\n\t0x27, 0x0a, 0x0e, 0x52, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x61, 0x73, 0x6f,\n\t0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63,\n\t0x6b, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75,\n\t0x6c, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74,\n\t0x22, 0x6d, 0x0a, 0x0b, 0x44, 0x74, 0x6d, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12,\n\t0x16, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,\n\t0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x19, 0x0a, 0x07, 0x42, 0x69, 0x6e, 0x44, 0x61,\n\t0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x62, 0x69, 0x6e, 0x5f, 0x64, 0x61,\n\t0x74, 0x61, 0x12, 0x1b, 0x0a, 0x08, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x49, 0x44, 0x18, 0x03,\n\t0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x69, 0x64, 0x12,\n\t0x0e, 0x0a, 0x02, 0x4f, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x6f, 0x70, 0x22,\n\t0x51, 0x0a, 0x0f, 0x44, 0x74, 0x6d, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65,\n\t0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x09, 0x52, 0x05, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x18,\n\t0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x16, 0x0a, 0x06, 0x52, 0x65,\n\t0x6d, 0x61, 0x72, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x52, 0x65, 0x6d, 0x61,\n\t0x72, 0x6b, 0x32, 0xbf, 0x04, 0x0a, 0x03, 0x44, 0x74, 0x6d, 0x12, 0x38, 0x0a, 0x06, 0x4e, 0x65,\n\t0x77, 0x47, 0x69, 0x64, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x64,\n\t0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x47, 0x69, 0x64, 0x52, 0x65, 0x70,\n\t0x6c, 0x79, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x06, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x12, 0x13,\n\t0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x52, 0x65, 0x71, 0x75,\n\t0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x38, 0x0a,\n\t0x07, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x12, 0x13, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69,\n\t0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e,\n\t0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,\n\t0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x05, 0x41, 0x62, 0x6f, 0x72, 0x74,\n\t0x12, 0x13, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12,\n\t0x45, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x42, 0x72, 0x61, 0x6e, 0x63,\n\t0x68, 0x12, 0x19, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x42,\n\t0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67,\n\t0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,\n\t0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0f, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72,\n\t0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x13, 0x2e, 0x64, 0x74, 0x6d, 0x67,\n\t0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b,\n\t0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x50, 0x72, 0x6f, 0x67,\n\t0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3f, 0x0a,\n\t0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x18, 0x2e, 0x64, 0x74, 0x6d,\n\t0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x41,\n\t0x0a, 0x0b, 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x18, 0x2e,\n\t0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x54, 0x6f, 0x70, 0x69, 0x63,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22,\n\t0x00, 0x12, 0x41, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x6f, 0x70, 0x69, 0x63,\n\t0x12, 0x18, 0x2e, 0x64, 0x74, 0x6d, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x44, 0x74, 0x6d, 0x54, 0x6f,\n\t0x70, 0x69, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f,\n\t0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,\n\t0x74, 0x79, 0x22, 0x00, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x64, 0x74, 0x6d, 0x67, 0x70, 0x62,\n\t0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_dtmgimp_proto_rawDescOnce sync.Once\n\tfile_dtmgimp_proto_rawDescData = file_dtmgimp_proto_rawDesc\n)\n\nfunc file_dtmgimp_proto_rawDescGZIP() []byte {\n\tfile_dtmgimp_proto_rawDescOnce.Do(func() {\n\t\tfile_dtmgimp_proto_rawDescData = protoimpl.X.CompressGZIP(file_dtmgimp_proto_rawDescData)\n\t})\n\treturn file_dtmgimp_proto_rawDescData\n}\n\nvar file_dtmgimp_proto_msgTypes = make([]protoimpl.MessageInfo, 11)\nvar file_dtmgimp_proto_goTypes = []interface{}{\n\t(*DtmTransOptions)(nil),    // 0: dtmgimp.DtmTransOptions\n\t(*DtmRequest)(nil),         // 1: dtmgimp.DtmRequest\n\t(*DtmGidReply)(nil),        // 2: dtmgimp.DtmGidReply\n\t(*DtmBranchRequest)(nil),   // 3: dtmgimp.DtmBranchRequest\n\t(*DtmProgressesReply)(nil), // 4: dtmgimp.DtmProgressesReply\n\t(*DtmTransaction)(nil),     // 5: dtmgimp.DtmTransaction\n\t(*DtmProgress)(nil),        // 6: dtmgimp.DtmProgress\n\t(*DtmTopicRequest)(nil),    // 7: dtmgimp.DtmTopicRequest\n\tnil,                        // 8: dtmgimp.DtmTransOptions.BranchHeadersEntry\n\tnil,                        // 9: dtmgimp.DtmRequest.ReqExtraEntry\n\tnil,                        // 10: dtmgimp.DtmBranchRequest.DataEntry\n\t(*emptypb.Empty)(nil),      // 11: google.protobuf.Empty\n}\nvar file_dtmgimp_proto_depIdxs = []int32{\n\t8,  // 0: dtmgimp.DtmTransOptions.BranchHeaders:type_name -> dtmgimp.DtmTransOptions.BranchHeadersEntry\n\t0,  // 1: dtmgimp.DtmRequest.TransOptions:type_name -> dtmgimp.DtmTransOptions\n\t9,  // 2: dtmgimp.DtmRequest.ReqExtra:type_name -> dtmgimp.DtmRequest.ReqExtraEntry\n\t10, // 3: dtmgimp.DtmBranchRequest.Data:type_name -> dtmgimp.DtmBranchRequest.DataEntry\n\t5,  // 4: dtmgimp.DtmProgressesReply.Transaction:type_name -> dtmgimp.DtmTransaction\n\t6,  // 5: dtmgimp.DtmProgressesReply.Progresses:type_name -> dtmgimp.DtmProgress\n\t11, // 6: dtmgimp.Dtm.NewGid:input_type -> google.protobuf.Empty\n\t1,  // 7: dtmgimp.Dtm.Submit:input_type -> dtmgimp.DtmRequest\n\t1,  // 8: dtmgimp.Dtm.Prepare:input_type -> dtmgimp.DtmRequest\n\t1,  // 9: dtmgimp.Dtm.Abort:input_type -> dtmgimp.DtmRequest\n\t3,  // 10: dtmgimp.Dtm.RegisterBranch:input_type -> dtmgimp.DtmBranchRequest\n\t1,  // 11: dtmgimp.Dtm.PrepareWorkflow:input_type -> dtmgimp.DtmRequest\n\t7,  // 12: dtmgimp.Dtm.Subscribe:input_type -> dtmgimp.DtmTopicRequest\n\t7,  // 13: dtmgimp.Dtm.Unsubscribe:input_type -> dtmgimp.DtmTopicRequest\n\t7,  // 14: dtmgimp.Dtm.DeleteTopic:input_type -> dtmgimp.DtmTopicRequest\n\t2,  // 15: dtmgimp.Dtm.NewGid:output_type -> dtmgimp.DtmGidReply\n\t11, // 16: dtmgimp.Dtm.Submit:output_type -> google.protobuf.Empty\n\t11, // 17: dtmgimp.Dtm.Prepare:output_type -> google.protobuf.Empty\n\t11, // 18: dtmgimp.Dtm.Abort:output_type -> google.protobuf.Empty\n\t11, // 19: dtmgimp.Dtm.RegisterBranch:output_type -> google.protobuf.Empty\n\t4,  // 20: dtmgimp.Dtm.PrepareWorkflow:output_type -> dtmgimp.DtmProgressesReply\n\t11, // 21: dtmgimp.Dtm.Subscribe:output_type -> google.protobuf.Empty\n\t11, // 22: dtmgimp.Dtm.Unsubscribe:output_type -> google.protobuf.Empty\n\t11, // 23: dtmgimp.Dtm.DeleteTopic:output_type -> google.protobuf.Empty\n\t15, // [15:24] is the sub-list for method output_type\n\t6,  // [6:15] is the sub-list for method input_type\n\t6,  // [6:6] is the sub-list for extension type_name\n\t6,  // [6:6] is the sub-list for extension extendee\n\t0,  // [0:6] is the sub-list for field type_name\n}\n\nfunc init() { file_dtmgimp_proto_init() }\nfunc file_dtmgimp_proto_init() {\n\tif File_dtmgimp_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_dtmgimp_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DtmTransOptions); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_dtmgimp_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DtmRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_dtmgimp_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DtmGidReply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_dtmgimp_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DtmBranchRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_dtmgimp_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DtmProgressesReply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_dtmgimp_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DtmTransaction); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_dtmgimp_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DtmProgress); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_dtmgimp_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*DtmTopicRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_dtmgimp_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   11,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_dtmgimp_proto_goTypes,\n\t\tDependencyIndexes: file_dtmgimp_proto_depIdxs,\n\t\tMessageInfos:      file_dtmgimp_proto_msgTypes,\n\t}.Build()\n\tFile_dtmgimp_proto = out.File\n\tfile_dtmgimp_proto_rawDesc = nil\n\tfile_dtmgimp_proto_goTypes = nil\n\tfile_dtmgimp_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "client/dtmgrpc/dtmgpb/dtmgimp.proto",
    "content": "syntax = \"proto3\";\n\noption go_package = \"./dtmgpb\";\nimport \"google/protobuf/empty.proto\";\n\npackage dtmgimp;\n\n// The dtm service definition.\nservice Dtm {\n  rpc NewGid(google.protobuf.Empty) returns (DtmGidReply) {}\n  rpc Submit(DtmRequest) returns (google.protobuf.Empty) {}\n  rpc Prepare(DtmRequest) returns (google.protobuf.Empty) {}\n  rpc Abort(DtmRequest) returns (google.protobuf.Empty) {}\n  rpc RegisterBranch(DtmBranchRequest) returns (google.protobuf.Empty) {}\n  rpc PrepareWorkflow(DtmRequest) returns (DtmProgressesReply) {}\n  rpc Subscribe(DtmTopicRequest) returns (google.protobuf.Empty){}\n  rpc Unsubscribe(DtmTopicRequest) returns (google.protobuf.Empty){}\n  rpc DeleteTopic(DtmTopicRequest) returns (google.protobuf.Empty){}\n}\n\nmessage DtmTransOptions {\n  bool WaitResult = 1;\n  int64 TimeoutToFail = 2;\n  int64 RetryInterval = 3;\n  // repeated string PassthroughHeaders = 4; // depreceated\n  map<string, string> BranchHeaders = 5;\n  int64 RequestTimeout = 6;\n  int64 RetryLimit = 7;\n}\n\n// DtmRequest request sent to dtm server\nmessage DtmRequest {\n  string Gid = 1;\n  string TransType = 2;\n  DtmTransOptions TransOptions = 3;\n  string CustomedData = 4;\n  repeated bytes BinPayloads = 5; // for Msg/Saga/Workflow branch payloads\n  string QueryPrepared = 6; // for Msg\n  string Steps = 7;\n  map<string, string> ReqExtra = 8;\n  string RollbackReason = 9;\n}\n\nmessage DtmGidReply {\n  string Gid = 1;\n}\n\nmessage DtmBranchRequest {\n  string Gid = 1;\n  string TransType = 2;\n  string BranchID = 3;\n  string Op = 4;\n  map<string, string> Data = 5;\n  bytes BusiPayload = 6;\n}\n\nmessage DtmProgressesReply {\n  DtmTransaction Transaction = 1 [json_name=\"transaction\"];\n  repeated DtmProgress Progresses = 2 [json_name=\"progresses\"];\n}\n\nmessage DtmTransaction {\n  string Gid = 1 [json_name=\"gid\"];\n  string Status = 2 [json_name=\"status\"];\n  string RollbackReason = 3 [json_name=\"rollback_reason\"];\n  string Result = 4 [json_name=\"result\"];\n}\n\nmessage DtmProgress {\n  string Status = 1 [json_name=\"status\"];\n  bytes BinData = 2 [json_name=\"bin_data\"];\n  string BranchID = 3 [json_name=\"branch_id\"];\n  string Op = 4 [json_name=\"op\"];\n}\n\nmessage DtmTopicRequest {\n  string Topic = 1;\n  string URL = 2;\n  string Remark = 3;\n}"
  },
  {
    "path": "client/dtmgrpc/dtmgpb/dtmgimp_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.2.0\n// - protoc             v3.21.12\n// source: dtmgimp.proto\n\npackage dtmgpb\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n\temptypb \"google.golang.org/protobuf/types/known/emptypb\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\n// Requires gRPC-Go v1.32.0 or later.\nconst _ = grpc.SupportPackageIsVersion7\n\n// DtmClient is the client API for Dtm service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\ntype DtmClient interface {\n\tNewGid(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DtmGidReply, error)\n\tSubmit(ctx context.Context, in *DtmRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tPrepare(ctx context.Context, in *DtmRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tAbort(ctx context.Context, in *DtmRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tRegisterBranch(ctx context.Context, in *DtmBranchRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tPrepareWorkflow(ctx context.Context, in *DtmRequest, opts ...grpc.CallOption) (*DtmProgressesReply, error)\n\tSubscribe(ctx context.Context, in *DtmTopicRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tUnsubscribe(ctx context.Context, in *DtmTopicRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tDeleteTopic(ctx context.Context, in *DtmTopicRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)\n}\n\ntype dtmClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewDtmClient(cc grpc.ClientConnInterface) DtmClient {\n\treturn &dtmClient{cc}\n}\n\nfunc (c *dtmClient) NewGid(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DtmGidReply, error) {\n\tout := new(DtmGidReply)\n\terr := c.cc.Invoke(ctx, \"/dtmgimp.Dtm/NewGid\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *dtmClient) Submit(ctx context.Context, in *DtmRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/dtmgimp.Dtm/Submit\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *dtmClient) Prepare(ctx context.Context, in *DtmRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/dtmgimp.Dtm/Prepare\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *dtmClient) Abort(ctx context.Context, in *DtmRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/dtmgimp.Dtm/Abort\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *dtmClient) RegisterBranch(ctx context.Context, in *DtmBranchRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/dtmgimp.Dtm/RegisterBranch\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *dtmClient) PrepareWorkflow(ctx context.Context, in *DtmRequest, opts ...grpc.CallOption) (*DtmProgressesReply, error) {\n\tout := new(DtmProgressesReply)\n\terr := c.cc.Invoke(ctx, \"/dtmgimp.Dtm/PrepareWorkflow\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *dtmClient) Subscribe(ctx context.Context, in *DtmTopicRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/dtmgimp.Dtm/Subscribe\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *dtmClient) Unsubscribe(ctx context.Context, in *DtmTopicRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/dtmgimp.Dtm/Unsubscribe\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *dtmClient) DeleteTopic(ctx context.Context, in *DtmTopicRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/dtmgimp.Dtm/DeleteTopic\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// DtmServer is the server API for Dtm service.\n// All implementations must embed UnimplementedDtmServer\n// for forward compatibility\ntype DtmServer interface {\n\tNewGid(context.Context, *emptypb.Empty) (*DtmGidReply, error)\n\tSubmit(context.Context, *DtmRequest) (*emptypb.Empty, error)\n\tPrepare(context.Context, *DtmRequest) (*emptypb.Empty, error)\n\tAbort(context.Context, *DtmRequest) (*emptypb.Empty, error)\n\tRegisterBranch(context.Context, *DtmBranchRequest) (*emptypb.Empty, error)\n\tPrepareWorkflow(context.Context, *DtmRequest) (*DtmProgressesReply, error)\n\tSubscribe(context.Context, *DtmTopicRequest) (*emptypb.Empty, error)\n\tUnsubscribe(context.Context, *DtmTopicRequest) (*emptypb.Empty, error)\n\tDeleteTopic(context.Context, *DtmTopicRequest) (*emptypb.Empty, error)\n\tmustEmbedUnimplementedDtmServer()\n}\n\n// UnimplementedDtmServer must be embedded to have forward compatible implementations.\ntype UnimplementedDtmServer struct {\n}\n\nfunc (UnimplementedDtmServer) NewGid(context.Context, *emptypb.Empty) (*DtmGidReply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method NewGid not implemented\")\n}\nfunc (UnimplementedDtmServer) Submit(context.Context, *DtmRequest) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method Submit not implemented\")\n}\nfunc (UnimplementedDtmServer) Prepare(context.Context, *DtmRequest) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method Prepare not implemented\")\n}\nfunc (UnimplementedDtmServer) Abort(context.Context, *DtmRequest) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method Abort not implemented\")\n}\nfunc (UnimplementedDtmServer) RegisterBranch(context.Context, *DtmBranchRequest) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method RegisterBranch not implemented\")\n}\nfunc (UnimplementedDtmServer) PrepareWorkflow(context.Context, *DtmRequest) (*DtmProgressesReply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method PrepareWorkflow not implemented\")\n}\nfunc (UnimplementedDtmServer) Subscribe(context.Context, *DtmTopicRequest) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method Subscribe not implemented\")\n}\nfunc (UnimplementedDtmServer) Unsubscribe(context.Context, *DtmTopicRequest) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method Unsubscribe not implemented\")\n}\nfunc (UnimplementedDtmServer) DeleteTopic(context.Context, *DtmTopicRequest) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method DeleteTopic not implemented\")\n}\nfunc (UnimplementedDtmServer) mustEmbedUnimplementedDtmServer() {}\n\n// UnsafeDtmServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to DtmServer will\n// result in compilation errors.\ntype UnsafeDtmServer interface {\n\tmustEmbedUnimplementedDtmServer()\n}\n\nfunc RegisterDtmServer(s grpc.ServiceRegistrar, srv DtmServer) {\n\ts.RegisterService(&Dtm_ServiceDesc, srv)\n}\n\nfunc _Dtm_NewGid_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(emptypb.Empty)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(DtmServer).NewGid(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/dtmgimp.Dtm/NewGid\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(DtmServer).NewGid(ctx, req.(*emptypb.Empty))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Dtm_Submit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(DtmRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(DtmServer).Submit(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/dtmgimp.Dtm/Submit\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(DtmServer).Submit(ctx, req.(*DtmRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Dtm_Prepare_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(DtmRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(DtmServer).Prepare(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/dtmgimp.Dtm/Prepare\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(DtmServer).Prepare(ctx, req.(*DtmRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Dtm_Abort_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(DtmRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(DtmServer).Abort(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/dtmgimp.Dtm/Abort\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(DtmServer).Abort(ctx, req.(*DtmRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Dtm_RegisterBranch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(DtmBranchRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(DtmServer).RegisterBranch(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/dtmgimp.Dtm/RegisterBranch\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(DtmServer).RegisterBranch(ctx, req.(*DtmBranchRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Dtm_PrepareWorkflow_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(DtmRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(DtmServer).PrepareWorkflow(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/dtmgimp.Dtm/PrepareWorkflow\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(DtmServer).PrepareWorkflow(ctx, req.(*DtmRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Dtm_Subscribe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(DtmTopicRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(DtmServer).Subscribe(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/dtmgimp.Dtm/Subscribe\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(DtmServer).Subscribe(ctx, req.(*DtmTopicRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Dtm_Unsubscribe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(DtmTopicRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(DtmServer).Unsubscribe(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/dtmgimp.Dtm/Unsubscribe\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(DtmServer).Unsubscribe(ctx, req.(*DtmTopicRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Dtm_DeleteTopic_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(DtmTopicRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(DtmServer).DeleteTopic(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/dtmgimp.Dtm/DeleteTopic\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(DtmServer).DeleteTopic(ctx, req.(*DtmTopicRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// Dtm_ServiceDesc is the grpc.ServiceDesc for Dtm service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar Dtm_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"dtmgimp.Dtm\",\n\tHandlerType: (*DtmServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"NewGid\",\n\t\t\tHandler:    _Dtm_NewGid_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"Submit\",\n\t\t\tHandler:    _Dtm_Submit_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"Prepare\",\n\t\t\tHandler:    _Dtm_Prepare_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"Abort\",\n\t\t\tHandler:    _Dtm_Abort_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"RegisterBranch\",\n\t\t\tHandler:    _Dtm_RegisterBranch_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"PrepareWorkflow\",\n\t\t\tHandler:    _Dtm_PrepareWorkflow_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"Subscribe\",\n\t\t\tHandler:    _Dtm_Subscribe_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"Unsubscribe\",\n\t\t\tHandler:    _Dtm_Unsubscribe_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"DeleteTopic\",\n\t\t\tHandler:    _Dtm_DeleteTopic_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"dtmgimp.proto\",\n}\n"
  },
  {
    "path": "client/dtmgrpc/msg.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmgrpc\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\tgrpc \"google.golang.org/grpc\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\n// MsgGrpc reliable msg type\ntype MsgGrpc struct {\n\tdtmcli.Msg\n}\n\n// NewMsgGrpc create new msg\nfunc NewMsgGrpc(server string, gid string, opts ...TransBaseOption) *MsgGrpc {\n\tmg := &MsgGrpc{Msg: *dtmcli.NewMsg(server, gid)}\n\n\tfor _, opt := range opts {\n\t\topt(&mg.TransBase)\n\t}\n\n\treturn mg\n}\n\n// Add add a new step\nfunc (s *MsgGrpc) Add(action string, msg proto.Message) *MsgGrpc {\n\ts.Steps = append(s.Steps, map[string]string{\"action\": action})\n\ts.BinPayloads = append(s.BinPayloads, dtmgimp.MustProtoMarshal(msg))\n\treturn s\n}\n\n// AddTopic add a new topic step\nfunc (s *MsgGrpc) AddTopic(topic string, msg proto.Message) *MsgGrpc {\n\treturn s.Add(fmt.Sprintf(\"%s%s\", dtmimp.MsgTopicPrefix, topic), msg)\n}\n\n// SetDelay delay call branch, unit second\nfunc (s *MsgGrpc) SetDelay(delay uint64) *MsgGrpc {\n\ts.Msg.SetDelay(delay)\n\treturn s\n}\n\n// Prepare prepare the msg, msg will later be submitted\nfunc (s *MsgGrpc) Prepare(queryPrepared string) error {\n\ts.QueryPrepared = dtmimp.OrString(queryPrepared, s.QueryPrepared)\n\treturn dtmgimp.DtmGrpcCall(&s.TransBase, \"Prepare\")\n}\n\n// Submit submit the msg\nfunc (s *MsgGrpc) Submit() error {\n\ts.Msg.BuildCustomOptions()\n\treturn dtmgimp.DtmGrpcCall(&s.TransBase, \"Submit\")\n}\n\n// DoAndSubmitDB short method for Do on db type. please see DoAndSubmit\nfunc (s *MsgGrpc) DoAndSubmitDB(queryPrepared string, db *sql.DB, busiCall dtmcli.BarrierBusiFunc) error {\n\treturn s.DoAndSubmit(queryPrepared, func(bb *dtmcli.BranchBarrier) error {\n\t\treturn bb.CallWithDB(db, busiCall)\n\t})\n}\n\n// DoAndSubmit one method for the entire prepare->busi->submit\n// the error returned by busiCall will be returned\n// if busiCall return ErrFailure, then abort is called directly\n// if busiCall return not nil error other than ErrFailure, then DoAndSubmit will call queryPrepared to get the result\nfunc (s *MsgGrpc) DoAndSubmit(queryPrepared string, busiCall func(bb *dtmcli.BranchBarrier) error, opts ...grpc.CallOption) error {\n\tbb, err := dtmcli.BarrierFrom(s.TransType, s.Gid, dtmimp.MsgDoBranch0, dtmimp.MsgDoOp) // a special barrier for msg QueryPrepared\n\tif err == nil {\n\t\terr = s.Prepare(queryPrepared)\n\t}\n\tif err == nil {\n\t\terrb := busiCall(bb)\n\t\tif errb != nil && !errors.Is(errb, dtmcli.ErrFailure) {\n\t\t\terr = dtmgimp.InvokeBranch(&s.TransBase, true, nil, queryPrepared, &[]byte{}, bb.BranchID, bb.Op, opts...)\n\t\t\terr = GrpcError2DtmError(err)\n\t\t}\n\t\tif errors.Is(errb, dtmcli.ErrFailure) || errors.Is(err, dtmcli.ErrFailure) {\n\t\t\t_ = dtmgimp.DtmGrpcCall(&s.TransBase, \"Abort\")\n\t\t} else if err == nil {\n\t\t\terr = s.Submit()\n\t\t}\n\t\tif errb != nil {\n\t\t\treturn errb\n\t\t}\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "client/dtmgrpc/options.go",
    "content": "package dtmgrpc\n\nimport (\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n)\n\n// TransBaseOption setup func for TransBase\ntype TransBaseOption func(tb *dtmimp.TransBase)\n\n// WithBranchHeaders setup TransBase.BranchHeaders\nfunc WithBranchHeaders(headers map[string]string) TransBaseOption {\n\treturn func(tb *dtmimp.TransBase) {\n\t\ttb.BranchHeaders = headers\n\t}\n}\n"
  },
  {
    "path": "client/dtmgrpc/options_test.go",
    "content": "package dtmgrpc\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n)\n\n// TestNewMsgGrpc ut for NewMsgGrpc\nfunc TestNewMsgGrpc(t *testing.T) {\n\tvar (\n\t\tserver            = \"dmt_server_address\"\n\t\tgidNoOptions      = \"msg_no_setup_options\"\n\t\tgidTraceIDXXX     = \"msg_setup_options_trace_id_xxx\"\n\t\tmsgWithTraceIDXXX = &MsgGrpc{Msg: *dtmcli.NewMsg(server, gidTraceIDXXX)}\n\t\ttraceIDHeaders    = map[string]string{\n\t\t\t\"x-trace-id\": \"xxx\",\n\t\t}\n\t)\n\tmsgWithTraceIDXXX.BranchHeaders = traceIDHeaders\n\ttype args struct {\n\t\tgid  string\n\t\topts []TransBaseOption\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant *MsgGrpc\n\t}{\n\t\t{\n\t\t\tname: \"no setup options\",\n\t\t\targs: args{gid: gidNoOptions},\n\t\t\twant: &MsgGrpc{Msg: *dtmcli.NewMsg(server, gidNoOptions)},\n\t\t},\n\t\t{\n\t\t\tname: \"msg with trace_id\",\n\t\t\targs: args{\n\t\t\t\tgid: gidTraceIDXXX,\n\t\t\t\topts: []TransBaseOption{\n\t\t\t\t\tWithBranchHeaders(traceIDHeaders),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: msgWithTraceIDXXX,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := NewMsgGrpc(server, tt.args.gid, tt.args.opts...)\n\t\t\tt.Logf(\"TestNewMsgGrpc %s got %+v\\n\", tt.name, got)\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"NewMsgGrpc() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestNewSagaGrpc ut for NewSagaGrpc\nfunc TestNewSagaGrpc(t *testing.T) {\n\tvar (\n\t\tserver             = \"dmt_server_address\"\n\t\tgidNoOptions       = \"msg_no_setup_options\"\n\t\tgidTraceIDXXX      = \"msg_setup_options_trace_id_xxx\"\n\t\tsagaWithTraceIDXXX = &SagaGrpc{Saga: *dtmcli.NewSaga(server, gidTraceIDXXX)}\n\t\ttraceIDHeaders     = map[string]string{\n\t\t\t\"x-trace-id\": \"xxx\",\n\t\t}\n\t)\n\tsagaWithTraceIDXXX.BranchHeaders = traceIDHeaders\n\ttype args struct {\n\t\tgid  string\n\t\topts []TransBaseOption\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant *SagaGrpc\n\t}{\n\t\t{\n\t\t\tname: \"no setup options\",\n\t\t\targs: args{gid: gidNoOptions},\n\t\t\twant: &SagaGrpc{Saga: *dtmcli.NewSaga(server, gidNoOptions)},\n\t\t},\n\t\t{\n\t\t\tname: \"msg with trace_id\",\n\t\t\targs: args{\n\t\t\t\tgid: gidTraceIDXXX,\n\t\t\t\topts: []TransBaseOption{\n\t\t\t\t\tWithBranchHeaders(traceIDHeaders),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: sagaWithTraceIDXXX,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := NewSagaGrpc(server, tt.args.gid, tt.args.opts...)\n\t\t\tt.Logf(\"TestNewSagaGrpc %s got %+v\\n\", tt.name, got)\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"NewSagaGrpc() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestNewSagaGrpcWithContext ut for NewSagaGrpcWithContext\nfunc TestNewSagaGrpcWithContext(t *testing.T) {\n\tvar (\n\t\tctx                = context.Background()\n\t\tserver             = \"dmt_server_address\"\n\t\tgidNoOptions       = \"msg_no_setup_options\"\n\t\tgidTraceIDXXX      = \"msg_setup_options_trace_id_xxx\"\n\t\tsagaWithTraceIDXXX = &SagaGrpc{Saga: *dtmcli.NewSagaWithContext(ctx, server, gidTraceIDXXX)}\n\t\ttraceIDHeaders     = map[string]string{\n\t\t\t\"x-trace-id\": \"xxx\",\n\t\t}\n\t)\n\tsagaWithTraceIDXXX.BranchHeaders = traceIDHeaders\n\ttype args struct {\n\t\tgid  string\n\t\topts []TransBaseOption\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant *SagaGrpc\n\t}{\n\t\t{\n\t\t\tname: \"no setup options\",\n\t\t\targs: args{gid: gidNoOptions},\n\t\t\twant: &SagaGrpc{Saga: *dtmcli.NewSaga(server, gidNoOptions)},\n\t\t},\n\t\t{\n\t\t\tname: \"msg with trace_id\",\n\t\t\targs: args{\n\t\t\t\tgid: gidTraceIDXXX,\n\t\t\t\topts: []TransBaseOption{\n\t\t\t\t\tWithBranchHeaders(traceIDHeaders),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: sagaWithTraceIDXXX,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := NewSagaGrpcWithContext(ctx, server, tt.args.gid, tt.args.opts...)\n\t\t\tt.Logf(\"TestNewSagaGrpc %s got %+v\\n\", tt.name, got)\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"NewSagaGrpc() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "client/dtmgrpc/saga.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmgrpc\n\nimport (\n\t\"context\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\n// SagaGrpc struct of saga\ntype SagaGrpc struct {\n\tdtmcli.Saga\n}\n\n// NewSagaGrpc create a saga\nfunc NewSagaGrpc(server string, gid string, opts ...TransBaseOption) *SagaGrpc {\n\tsg := &SagaGrpc{Saga: *dtmcli.NewSaga(server, gid)}\n\n\tfor _, opt := range opts {\n\t\topt(&sg.TransBase)\n\t}\n\n\treturn sg\n}\n\n// NewSagaGrpcWithContext create a saga with context\nfunc NewSagaGrpcWithContext(ctx context.Context, server string, gid string, opts ...TransBaseOption) *SagaGrpc {\n\tsg := &SagaGrpc{Saga: *dtmcli.NewSagaWithContext(ctx, server, gid)}\n\n\tfor _, opt := range opts {\n\t\topt(&sg.TransBase)\n\t}\n\n\treturn sg\n}\n\n// Add add a saga step\nfunc (s *SagaGrpc) Add(action string, compensate string, payload proto.Message) *SagaGrpc {\n\ts.Steps = append(s.Steps, map[string]string{\"action\": action, \"compensate\": compensate})\n\ts.BinPayloads = append(s.BinPayloads, dtmgimp.MustProtoMarshal(payload))\n\treturn s\n}\n\n// AddBranchOrder specify that branch should be after preBranches. branch should is larger than all the element in preBranches\nfunc (s *SagaGrpc) AddBranchOrder(branch int, preBranches []int) *SagaGrpc {\n\ts.Saga.AddBranchOrder(branch, preBranches)\n\treturn s\n}\n\n// EnableConcurrent enable the concurrent exec of sub trans\nfunc (s *SagaGrpc) EnableConcurrent() *SagaGrpc {\n\ts.Saga.SetConcurrent()\n\treturn s\n}\n\n// Submit submit the saga trans\nfunc (s *SagaGrpc) Submit() error {\n\ts.Saga.BuildCustomOptions()\n\treturn dtmgimp.DtmGrpcCall(&s.Saga.TransBase, \"Submit\")\n}\n"
  },
  {
    "path": "client/dtmgrpc/tcc.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmgrpc\n\nimport (\n\tcontext \"context\"\n\t\"fmt\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgpb\"\n\tgrpc \"google.golang.org/grpc\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\n// TccGrpc struct of tcc\ntype TccGrpc struct {\n\tdtmimp.TransBase\n}\n\n// TccGlobalFunc type of global tcc call\ntype TccGlobalFunc func(tcc *TccGrpc) error\n\n// TccGlobalTransaction begin a tcc global transaction\n// dtm dtm server url\n// gid global transaction id\n// tccFunc tcc busi func, define the transaction logic\nfunc TccGlobalTransaction(dtm string, gid string, tccFunc TccGlobalFunc) (rerr error) {\n\treturn TccGlobalTransaction2(dtm, gid, func(tg *TccGrpc) {}, tccFunc)\n}\n\n// TccGlobalTransaction2 new version of TccGlobalTransaction\nfunc TccGlobalTransaction2(dtm string, gid string, custom func(*TccGrpc), tccFunc TccGlobalFunc) (rerr error) {\n\ttcc := &TccGrpc{TransBase: *dtmimp.NewTransBase(gid, \"tcc\", dtm, \"\")}\n\tcustom(tcc)\n\trerr = dtmgimp.DtmGrpcCall(&tcc.TransBase, \"Prepare\")\n\tif rerr != nil {\n\t\treturn rerr\n\t}\n\tdefer dtmimp.DeferDo(&rerr, func() error {\n\t\treturn dtmgimp.DtmGrpcCall(&tcc.TransBase, \"Submit\")\n\t}, func() error {\n\t\ttcc.RollbackReason = rerr.Error()\n\t\treturn dtmgimp.DtmGrpcCall(&tcc.TransBase, \"Abort\")\n\t})\n\treturn tccFunc(tcc)\n}\n\n// TccFromGrpc tcc from request info\nfunc TccFromGrpc(ctx context.Context) (*TccGrpc, error) {\n\ttcc := &TccGrpc{\n\t\tTransBase: *dtmgimp.TransBaseFromGrpc(ctx),\n\t}\n\tif tcc.Dtm == \"\" || tcc.Gid == \"\" {\n\t\treturn nil, fmt.Errorf(\"bad tcc info. dtm: %s, gid: %s branchid: %s\", tcc.Dtm, tcc.Gid, tcc.BranchID)\n\t}\n\treturn tcc, nil\n}\n\n// CallBranch call a tcc branch\nfunc (t *TccGrpc) CallBranch(busiMsg proto.Message, tryURL string, confirmURL string, cancelURL string, reply interface{}, opts ...grpc.CallOption) error {\n\tbranchID := t.NewSubBranchID()\n\tbd, err := proto.Marshal(busiMsg)\n\tif err == nil {\n\t\t_, err = dtmgimp.MustGetDtmClient(t.Dtm).RegisterBranch(context.Background(), &dtmgpb.DtmBranchRequest{\n\t\t\tGid:         t.Gid,\n\t\t\tTransType:   t.TransType,\n\t\t\tBranchID:    branchID,\n\t\t\tBusiPayload: bd,\n\t\t\tData:        map[string]string{\"confirm\": confirmURL, \"cancel\": cancelURL},\n\t\t})\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn dtmgimp.InvokeBranch(&t.TransBase, false, busiMsg, tryURL, reply, branchID, \"try\", opts...)\n}\n"
  },
  {
    "path": "client/dtmgrpc/type.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmgrpc\n\nimport (\n\tcontext \"context\"\n\t\"errors\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\t\"github.com/dtm-labs/dtmdriver\"\n\tgrpc \"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n\temptypb \"google.golang.org/protobuf/types/known/emptypb\"\n)\n\n// DtmError2GrpcError translate dtm error to grpc error\nfunc DtmError2GrpcError(res interface{}) error {\n\te, ok := res.(error)\n\tif ok && errors.Is(e, dtmimp.ErrFailure) {\n\t\treturn status.New(codes.Aborted, e.Error()).Err()\n\t} else if ok && errors.Is(e, dtmimp.ErrOngoing) {\n\t\treturn status.New(codes.FailedPrecondition, e.Error()).Err()\n\t}\n\treturn e\n}\n\n// GrpcError2DtmError translate grpc error to dtm error\nfunc GrpcError2DtmError(err error) error {\n\tst, _ := status.FromError(err)\n\tif st != nil && st.Code() == codes.Aborted {\n\t\t// version lower then v1.10, will specify Ongoing in code Aborted\n\t\tif st.Message() == dtmcli.ResultOngoing {\n\t\t\treturn dtmcli.ErrOngoing\n\t\t}\n\t\treturn dtmcli.ErrorMessage2Error(st.Message(), dtmcli.ErrFailure)\n\t} else if st != nil && st.Code() == codes.FailedPrecondition {\n\t\treturn dtmcli.ErrorMessage2Error(st.Message(), dtmcli.ErrOngoing)\n\t}\n\treturn err\n}\n\n// MustGenGid must gen a gid from grpcServer\nfunc MustGenGid(grpcServer string) string {\n\tdc := dtmgimp.MustGetDtmClient(grpcServer)\n\tr, err := dc.NewGid(context.Background(), &emptypb.Empty{})\n\tdtmimp.E2P(err)\n\treturn r.Gid\n}\n\n// UseDriver use the specified driver to handle grpc urls\nfunc UseDriver(driverName string) error {\n\treturn dtmdriver.Use(driverName)\n}\n\n// AddUnaryInterceptor adds grpc.UnaryClientInterceptor\nfunc AddUnaryInterceptor(interceptor grpc.UnaryClientInterceptor) {\n\tdtmgimp.ClientInterceptors = append(dtmgimp.ClientInterceptors, interceptor)\n}\n"
  },
  {
    "path": "client/dtmgrpc/type_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmgrpc\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestType(t *testing.T) {\n\t_, err := BarrierFromGrpc(context.Background())\n\tassert.Error(t, err)\n\n\t_, err = TccFromGrpc(context.Background())\n\tassert.Error(t, err)\n\n\terr = UseDriver(\"default\")\n\tassert.Nil(t, err)\n}\n"
  },
  {
    "path": "client/dtmgrpc/xa.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmgrpc\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgpb\"\n\tgrpc \"google.golang.org/grpc\"\n\t\"google.golang.org/protobuf/proto\"\n\temptypb \"google.golang.org/protobuf/types/known/emptypb\"\n)\n\n// XaGrpcGlobalFunc type of xa global function\ntype XaGrpcGlobalFunc func(xa *XaGrpc) error\n\n// XaGrpcLocalFunc type of xa local function\ntype XaGrpcLocalFunc func(db *sql.DB, xa *XaGrpc) error\n\n// XaGrpc xa transaction\ntype XaGrpc struct {\n\tdtmimp.TransBase\n\tPhase2URL string\n}\n\n// XaGrpcFromRequest construct xa info from request\nfunc XaGrpcFromRequest(ctx context.Context) (*XaGrpc, error) {\n\txa := &XaGrpc{\n\t\tTransBase: *dtmgimp.TransBaseFromGrpc(ctx),\n\t}\n\txa.Phase2URL = dtmgimp.GetDtmMetaFromContext(ctx, \"phase2_url\")\n\tif xa.Gid == \"\" || xa.BranchID == \"\" || xa.Op == \"\" {\n\t\treturn nil, fmt.Errorf(\"bad xa info: gid: %s branchid: %s op: %s phase2_url: %s\", xa.Gid, xa.BranchID, xa.Op, xa.Phase2URL)\n\t}\n\treturn xa, nil\n}\n\n// XaLocalTransaction start a xa local transaction\nfunc XaLocalTransaction(ctx context.Context, dbConf dtmcli.DBConf, xaFunc XaGrpcLocalFunc) error {\n\txa, err := XaGrpcFromRequest(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif xa.Op == dtmimp.OpCommit || xa.Op == dtmimp.OpRollback {\n\t\treturn dtmimp.XaHandlePhase2(xa.Gid, dbConf, xa.BranchID, xa.Op)\n\t}\n\treturn dtmimp.XaHandleLocalTrans(&xa.TransBase, dbConf, func(db *sql.DB) error {\n\t\terr := xaFunc(db, xa)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = dtmgimp.MustGetDtmClient(xa.Dtm).RegisterBranch(context.Background(), &dtmgpb.DtmBranchRequest{\n\t\t\tGid:         xa.Gid,\n\t\t\tBranchID:    xa.BranchID,\n\t\t\tTransType:   xa.TransType,\n\t\t\tBusiPayload: nil,\n\t\t\tData:        map[string]string{\"url\": xa.Phase2URL},\n\t\t})\n\t\treturn err\n\t})\n}\n\n// XaGlobalTransaction start a xa global transaction\nfunc XaGlobalTransaction(server string, gid string, xaFunc XaGrpcGlobalFunc) error {\n\treturn XaGlobalTransaction2(server, gid, func(xg *XaGrpc) {}, xaFunc)\n}\n\n// XaGlobalTransaction2 new version of XaGlobalTransaction. support custom\nfunc XaGlobalTransaction2(server string, gid string, custom func(*XaGrpc), xaFunc XaGrpcGlobalFunc) error {\n\txa := &XaGrpc{TransBase: *dtmimp.NewTransBase(gid, \"xa\", server, \"\")}\n\tcustom(xa)\n\tdc := dtmgimp.MustGetDtmClient(xa.Dtm)\n\treq := dtmgimp.GetDtmRequest(&xa.TransBase)\n\treturn dtmimp.XaHandleGlobalTrans(&xa.TransBase, func(action string) error {\n\t\tf := map[string]func(context.Context, *dtmgpb.DtmRequest, ...grpc.CallOption) (*emptypb.Empty, error){\n\t\t\t\"prepare\": dc.Prepare,\n\t\t\t\"submit\":  dc.Submit,\n\t\t\t\"abort\":   dc.Abort,\n\t\t}[action]\n\t\t_, err := f(context.Background(), req)\n\t\treturn err\n\t}, func() error {\n\t\treturn xaFunc(xa)\n\t})\n}\n\n// CallBranch call a xa branch\nfunc (x *XaGrpc) CallBranch(msg proto.Message, url string, reply interface{}, opts ...grpc.CallOption) error {\n\treturn dtmgimp.InvokeBranch(&x.TransBase, false, msg, url, reply, x.NewSubBranchID(), \"action\", opts...)\n}\n"
  },
  {
    "path": "client/workflow/dummyReadCloser.go",
    "content": "package workflow\n\nimport (\n\t\"bytes\"\n\t\"io\"\n)\n\n// NewRespBodyFromBytes creates an io.ReadCloser from a byte slice\n// that is suitable for use as an http response body.\nfunc NewRespBodyFromBytes(body []byte) io.ReadCloser {\n\treturn &dummyReadCloser{body: bytes.NewReader(body)}\n}\n\ntype dummyReadCloser struct {\n\tbody io.ReadSeeker\n}\n\nfunc (d *dummyReadCloser) Read(p []byte) (n int, err error) {\n\treturn d.body.Read(p)\n}\n\nfunc (d *dummyReadCloser) Close() error {\n\t_, _ = d.body.Seek(0, io.SeekEnd)\n\treturn nil\n}\n"
  },
  {
    "path": "client/workflow/factory.go",
    "content": "package workflow\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/dtm-labs/logger\"\n)\n\ntype workflowFactory struct {\n\tprotocol     string\n\thttpDtm      string\n\thttpCallback string\n\tgrpcDtm      string\n\tgrpcCallback string\n\thandlers     map[string]*wfItem\n}\n\nvar defaultFac = workflowFactory{\n\thandlers: map[string]*wfItem{},\n}\n\nfunc (w *workflowFactory) execute(ctx context.Context, name string, gid string, data []byte) ([]byte, error) {\n\thandler := w.handlers[name]\n\tif handler == nil {\n\t\treturn nil, fmt.Errorf(\"workflow '%s' not registered. please register at startup\", name)\n\t}\n\twf := w.newWorkflow(ctx, name, gid, data)\n\tfor _, fn := range handler.custom {\n\t\tfn(wf)\n\t}\n\treturn wf.process(handler.fn, data)\n}\n\nfunc (w *workflowFactory) register(name string, handler WfFunc2, custom ...func(wf *Workflow)) error {\n\te := w.handlers[name]\n\tif e != nil {\n\t\treturn fmt.Errorf(\"a handler already exists for %s\", name)\n\t}\n\tlogger.Debugf(\"workflow '%s' registered.\", name)\n\tw.handlers[name] = &wfItem{\n\t\tfn:     handler,\n\t\tcustom: custom,\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "client/workflow/imp.go",
    "content": "package workflow\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgpb\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/go-resty/resty/v2\"\n)\n\ntype workflowImp struct {\n\trestyClient          *resty.Client //nolint\n\tidGen                dtmimp.BranchIDGen\n\tcurrentBranch        string                 //nolint\n\tcurrentActionAdded   bool                   //nolint\n\tcurrentCommitAdded   bool                   //nolint\n\tcurrentRollbackAdded bool                   //nolint\n\tprogresses           map[string]*stepResult //nolint\n\tcurrentOp            string\n\tsucceededOps         []workflowPhase2Item\n\tfailedOps            []workflowPhase2Item\n}\n\ntype workflowPhase2Item struct {\n\tbranchID, op string\n\tfn           WfPhase2Func\n}\n\nfunc (wf *Workflow) initProgress(progresses []*dtmgpb.DtmProgress) {\n\twf.progresses = map[string]*stepResult{}\n\tfor _, p := range progresses {\n\t\tsr := &stepResult{\n\t\t\tStatus: p.Status,\n\t\t\tData:   p.BinData,\n\t\t}\n\t\tif sr.Status == dtmcli.StatusFailed {\n\t\t\tsr.Error = dtmcli.ErrorMessage2Error(string(p.BinData), dtmcli.ErrFailure)\n\t\t}\n\t\twf.progresses[p.BranchID+\"-\"+p.Op] = sr\n\t}\n}\n\ntype wfMeta struct{}\n\nfunc (w *workflowFactory) newWorkflow(ctx context.Context, name string, gid string, data []byte) *Workflow {\n\twf := &Workflow{\n\t\tTransBase: dtmimp.NewTransBase(gid, \"workflow\", \"not inited\", \"\"),\n\t\tName:      name,\n\t\tworkflowImp: workflowImp{\n\t\t\tidGen:        dtmimp.BranchIDGen{},\n\t\t\tsucceededOps: []workflowPhase2Item{},\n\t\t\tfailedOps:    []workflowPhase2Item{},\n\t\t\tcurrentOp:    dtmimp.OpAction,\n\t\t},\n\t}\n\twf.Context = ctx\n\twf.Protocol = w.protocol\n\tif w.protocol == dtmimp.ProtocolGRPC {\n\t\twf.Dtm = w.grpcDtm\n\t\twf.QueryPrepared = w.grpcCallback\n\t} else {\n\t\twf.Dtm = w.httpDtm\n\t\twf.QueryPrepared = w.httpCallback\n\t}\n\twf.CustomData = dtmimp.MustMarshalString(map[string]interface{}{\n\t\t\"name\": wf.Name,\n\t\t\"data\": data,\n\t})\n\twf.Context = context.WithValue(wf.Context, wfMeta{}, wf)\n\twf.Options.HTTPResp2DtmError = HTTPResp2DtmError\n\twf.Options.GRPCError2DtmError = GrpcError2DtmError\n\twf.initRestyClient()\n\treturn wf\n}\n\nfunc (wf *Workflow) initRestyClient() {\n\twf.restyClient = resty.New()\n\twf.restyClient.OnBeforeRequest(func(c *resty.Client, r *resty.Request) error {\n\t\tr.SetQueryParams(map[string]string{\n\t\t\t\"gid\":        wf.Gid,\n\t\t\t\"trans_type\": wf.TransType,\n\t\t\t\"branch_id\":  wf.currentBranch,\n\t\t\t\"op\":         wf.currentOp,\n\t\t})\n\t\treturn nil\n\t})\n\tdtmimp.AddRestyMiddlewares(wf.restyClient)\n\told := wf.restyClient.GetClient().Transport\n\twf.restyClient.GetClient().Transport = newRoundTripper(old, wf)\n}\n\nfunc (wf *Workflow) process(handler WfFunc2, data []byte) (res []byte, err error) {\n\treply, err2 := wf.getProgress()\n\tif err2 != nil {\n\t\treturn nil, err2\n\t}\n\n\tstatus := reply.Transaction.Status\n\tif status == dtmcli.StatusSucceed {\n\t\treturn base64.StdEncoding.DecodeString(reply.Transaction.Result)\n\t} else if status == dtmcli.StatusFailed {\n\t\treturn nil, dtmcli.ErrorMessage2Error(reply.Transaction.RollbackReason, dtmcli.ErrFailure)\n\t}\n\twf.initProgress(reply.Progresses)\n\tres, err = handler(wf, data)\n\terr = wf.Options.GRPCError2DtmError(err)\n\tif err != nil && !errors.Is(err, dtmcli.ErrFailure) {\n\t\treturn\n\t}\n\terr = wf.processPhase2(err)\n\n\tif err == nil || errors.Is(err, dtmcli.ErrFailure) {\n\t\terr1 := wf.submit(res, err)\n\t\tif err1 != nil {\n\t\t\treturn nil, err1\n\t\t}\n\t}\n\treturn\n}\n\nfunc (wf *Workflow) saveResult(branchID string, op string, sr *stepResult) error {\n\tif sr.Status != \"\" {\n\t\terr := wf.registerBranch(sr.Data, branchID, op, sr.Status)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn sr.Error\n}\n\nfunc (wf *Workflow) processPhase2(err error) error {\n\tops := wf.succeededOps\n\tif err == nil {\n\t\twf.currentOp = dtmimp.OpCommit\n\t} else {\n\t\twf.currentOp = dtmimp.OpRollback\n\t\tops = wf.failedOps\n\t}\n\tfor i := len(ops) - 1; i >= 0; i-- {\n\t\top := ops[i]\n\n\t\terr1 := wf.callPhase2(op.branchID, op.fn)\n\t\tif err1 != nil {\n\t\t\treturn err1\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (wf *Workflow) callPhase2(branchID string, fn WfPhase2Func) error {\n\twf.currentBranch = branchID\n\tr := wf.recordedDo(func(bb *dtmcli.BranchBarrier) *stepResult {\n\t\terr := fn(bb)\n\t\tdtmimp.PanicIf(errors.Is(err, dtmcli.ErrFailure), errors.New(\"should not return ErrFail in phase2\"))\n\t\treturn wf.stepResultFromLocal(nil, err)\n\t})\n\t_, err := wf.stepResultToLocal(r)\n\treturn err\n}\n\nfunc (wf *Workflow) recordedDo(fn func(bb *dtmcli.BranchBarrier) *stepResult) *stepResult {\n\tsr := wf.recordedDoInner(fn)\n\t// donot compensate the failed branch if !CompensateErrorBranch\n\tif !wf.Options.CompensateErrorBranch && sr.Status == dtmcli.StatusFailed {\n\t\tlastFailed := len(wf.failedOps) - 1\n\t\tif lastFailed >= 0 && wf.failedOps[lastFailed].branchID == wf.currentBranch {\n\t\t\twf.failedOps = wf.failedOps[:lastFailed]\n\t\t}\n\t}\n\treturn sr\n}\n\nfunc (wf *Workflow) recordedDoInner(fn func(bb *dtmcli.BranchBarrier) *stepResult) *stepResult {\n\tbranchID := wf.currentBranch\n\tif wf.currentOp == dtmimp.OpAction {\n\t\tdtmimp.PanicIf(wf.currentActionAdded, fmt.Errorf(\"one branch can have only on action\"))\n\t\twf.currentActionAdded = true\n\t}\n\tr := wf.getStepResult()\n\tif r != nil {\n\t\tlogger.Debugf(\"progress restored: '%s' '%s' '%v' '%s' '%s'\", branchID, wf.currentOp, r.Error, r.Status, r.Data)\n\t\treturn r\n\t}\n\tbb := &dtmcli.BranchBarrier{\n\t\tTransType: wf.TransType,\n\t\tGid:       wf.Gid,\n\t\tBranchID:  branchID,\n\t\tOp:        wf.currentOp,\n\t}\n\tr = fn(bb)\n\terr := wf.saveResult(branchID, wf.currentOp, r)\n\tif err != nil {\n\t\tr = wf.stepResultFromLocal(nil, err)\n\t}\n\treturn r\n}\n\nfunc (wf *Workflow) getStepResult() *stepResult {\n\tlogger.Debugf(\"getStepResult: %s %v\", wf.currentBranch+\"-\"+wf.currentOp, wf.progresses[wf.currentBranch+\"-\"+wf.currentOp])\n\treturn wf.progresses[wf.currentBranch+\"-\"+wf.currentOp]\n}\n"
  },
  {
    "path": "client/workflow/rpc.go",
    "content": "package workflow\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/logger\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgpb\"\n\t\"google.golang.org/protobuf/encoding/protojson\"\n\t\"google.golang.org/protobuf/types/known/emptypb\"\n)\n\nfunc (wf *Workflow) getProgress() (*dtmgpb.DtmProgressesReply, error) {\n\tif wf.Protocol == dtmimp.ProtocolGRPC {\n\t\tvar reply dtmgpb.DtmProgressesReply\n\t\terr := dtmgimp.MustGetGrpcConn(wf.Dtm, false).Invoke(wf.Context, \"/dtmgimp.Dtm/PrepareWorkflow\",\n\t\t\tdtmgimp.GetDtmRequest(wf.TransBase), &reply)\n\t\treturn &reply, err\n\t}\n\tresp, err := dtmcli.GetRestyClient().R().SetBody(wf.TransBase).Post(wf.Dtm + \"/prepareWorkflow\")\n\tvar reply dtmgpb.DtmProgressesReply\n\tif err == nil {\n\t\tuo := protojson.UnmarshalOptions{\n\t\t\tDiscardUnknown: true,\n\t\t}\n\t\terr = uo.Unmarshal(resp.Body(), &reply)\n\t}\n\tlogger.Infof(resp.String())\n\treturn &reply, err\n}\n\nfunc (wf *Workflow) submit(result []byte, err error) error {\n\tstatus := wfErrorToStatus(err)\n\treason := \"\"\n\tif err != nil {\n\t\treason = err.Error()\n\t}\n\textra := map[string]string{\n\t\t\"status\":          status,\n\t\t\"rollback_reason\": reason,\n\t\t\"result\":          base64.StdEncoding.EncodeToString(result),\n\t}\n\tif wf.Protocol == dtmimp.ProtocolHTTP {\n\t\tm := map[string]interface{}{\n\t\t\t\"gid\":        wf.Gid,\n\t\t\t\"trans_type\": wf.TransType,\n\t\t\t\"req_extra\":  extra,\n\t\t}\n\t\t_, err := dtmimp.TransCallDtmExt(wf.TransBase, m, \"submit\")\n\t\treturn err\n\t}\n\treq := dtmgimp.GetDtmRequest(wf.TransBase)\n\treq.ReqExtra = extra\n\treply := emptypb.Empty{}\n\treturn dtmgimp.MustGetGrpcConn(wf.Dtm, false).Invoke(wf.Context, \"/dtmgimp.Dtm/\"+\"Submit\", req, &reply)\n}\n\nfunc (wf *Workflow) registerBranch(res []byte, branchID string, op string, status string) error {\n\tif wf.Protocol == dtmimp.ProtocolHTTP {\n\t\treturn dtmimp.TransRegisterBranch(wf.TransBase, map[string]string{\n\t\t\t\"data\":      string(res),\n\t\t\t\"branch_id\": branchID,\n\t\t\t\"op\":        op,\n\t\t\t\"status\":    status,\n\t\t}, \"registerBranch\")\n\t}\n\t_, err := dtmgimp.MustGetDtmClient(wf.Dtm).RegisterBranch(context.Background(), &dtmgpb.DtmBranchRequest{\n\t\tGid:         wf.Gid,\n\t\tTransType:   wf.TransType,\n\t\tBranchID:    branchID,\n\t\tBusiPayload: res,\n\t\tData:        map[string]string{\"status\": status, \"op\": op},\n\t})\n\treturn err\n}\n"
  },
  {
    "path": "client/workflow/server.go",
    "content": "package workflow\n\nimport (\n\t\"context\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\t\"github.com/dtm-labs/dtm/client/workflow/wfpb\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n\t\"google.golang.org/protobuf/types/known/emptypb\"\n)\n\ntype workflowServer struct {\n\twfpb.UnimplementedWorkflowServer\n}\n\nfunc (s *workflowServer) Execute(ctx context.Context, wd *wfpb.WorkflowData) (*emptypb.Empty, error) {\n\tif defaultFac.protocol != dtmimp.ProtocolGRPC {\n\t\treturn nil, status.Errorf(codes.Internal, \"workflow server not inited. please call workflow.InitGrpc first\")\n\t}\n\ttb := dtmgimp.TransBaseFromGrpc(ctx)\n\t_, err := defaultFac.execute(ctx, tb.Op, tb.Gid, wd.Data)\n\treturn &emptypb.Empty{}, dtmgrpc.DtmError2GrpcError(err)\n}\n"
  },
  {
    "path": "client/workflow/utils.go",
    "content": "package workflow\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"strconv\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n\t\"google.golang.org/protobuf/reflect/protoreflect\"\n)\n\nfunc wfErrorToStatus(err error) string {\n\tif err == nil {\n\t\treturn dtmcli.StatusSucceed\n\t} else if errors.Is(err, dtmcli.ErrFailure) {\n\t\treturn dtmcli.StatusFailed\n\t}\n\treturn \"\"\n}\n\ntype stepResult struct {\n\tError  error  // if Error != nil || Status == \"\", result will not be saved\n\tStatus string // succeed | failed | \"\"\n\t// if status == succeed, data is the result.\n\t// if status == failed, data is the error message\n\tData []byte\n}\n\ntype roundTripper struct {\n\told http.RoundTripper\n\twf  *Workflow\n}\n\nfunc newJSONResponse(status int, result []byte) *http.Response {\n\treturn &http.Response{\n\t\tStatus:     strconv.Itoa(status),\n\t\tStatusCode: status,\n\t\tBody:       NewRespBodyFromBytes(result),\n\t\tHeader: http.Header{\n\t\t\t\"Content-Type\": []string{\"application/json\"},\n\t\t},\n\t\tContentLength: -1,\n\t}\n}\n\nfunc (r *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) {\n\twf := r.wf\n\torigin := func(bb *dtmcli.BranchBarrier) *stepResult {\n\t\tresp, err := r.old.RoundTrip(req)\n\t\treturn wf.stepResultFromHTTP(resp, err)\n\t}\n\tvar sr *stepResult\n\tif wf.currentOp != dtmimp.OpAction { // in phase 2, do not save, because it is saved outer\n\t\tsr = origin(nil)\n\t} else {\n\t\tsr = wf.recordedDo(origin)\n\t}\n\treturn wf.stepResultToHTTP(sr)\n}\n\nfunc newRoundTripper(old http.RoundTripper, wf *Workflow) http.RoundTripper {\n\treturn &roundTripper{old: old, wf: wf}\n}\n\n// HTTPResp2DtmError check for dtm error and return it\nfunc HTTPResp2DtmError(resp *http.Response) ([]byte, error) {\n\tcode := resp.StatusCode\n\tdata, err := ioutil.ReadAll(resp.Body)\n\tresp.Body = ioutil.NopCloser(bytes.NewBuffer(data))\n\tif code == http.StatusTooEarly {\n\t\treturn data, dtmcli.ErrorMessage2Error(string(data), dtmcli.ErrOngoing)\n\t} else if code == http.StatusConflict {\n\t\treturn data, dtmcli.ErrorMessage2Error(string(data), dtmcli.ErrFailure)\n\t} else if err == nil && code != http.StatusOK {\n\t\treturn data, errors.New(string(data))\n\t}\n\treturn data, err\n}\n\n// GrpcError2DtmError translate grpc error to dtm error\nfunc GrpcError2DtmError(err error) error {\n\tst, _ := status.FromError(err)\n\tif st != nil && st.Code() == codes.Aborted {\n\t\treturn dtmcli.ErrorMessage2Error(st.Message(), dtmcli.ErrFailure)\n\t} else if st != nil && st.Code() == codes.FailedPrecondition {\n\t\treturn dtmcli.ErrorMessage2Error(st.Message(), dtmcli.ErrOngoing)\n\t}\n\treturn err\n}\n\nfunc (wf *Workflow) stepResultFromLocal(data []byte, err error) *stepResult {\n\treturn &stepResult{\n\t\tError:  err,\n\t\tStatus: wfErrorToStatus(err),\n\t\tData:   data,\n\t}\n}\n\nfunc (wf *Workflow) stepResultToLocal(sr *stepResult) ([]byte, error) {\n\treturn sr.Data, sr.Error\n}\n\nfunc (wf *Workflow) stepResultFromGrpc(reply interface{}, err error) *stepResult {\n\tsr := &stepResult{Error: wf.Options.GRPCError2DtmError(err)}\n\tsr.Status = wfErrorToStatus(sr.Error)\n\tif sr.Error == nil {\n\t\tsr.Data = dtmgimp.MustProtoMarshal(reply.(protoreflect.ProtoMessage))\n\t} else if sr.Status == dtmcli.StatusFailed {\n\t\tsr.Data = []byte(err.Error())\n\t}\n\treturn sr\n}\n\nfunc (wf *Workflow) stepResultToGrpc(s *stepResult, reply interface{}) error {\n\tif s.Error == nil && s.Status == dtmcli.StatusSucceed {\n\t\tdtmgimp.MustProtoUnmarshal(s.Data, reply.(protoreflect.ProtoMessage))\n\t}\n\treturn s.Error\n}\n\nfunc (wf *Workflow) stepResultFromHTTP(resp *http.Response, err error) *stepResult {\n\tsr := &stepResult{Error: err}\n\tif err == nil {\n\t\tsr.Data, sr.Error = wf.Options.HTTPResp2DtmError(resp)\n\t\tsr.Status = wfErrorToStatus(sr.Error)\n\t}\n\treturn sr\n}\n\nfunc (wf *Workflow) stepResultToHTTP(s *stepResult) (*http.Response, error) {\n\tif s.Error != nil {\n\t\treturn nil, s.Error\n\t}\n\treturn newJSONResponse(200, s.Data), nil\n}\n"
  },
  {
    "path": "client/workflow/wfpb/wf.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.28.0\n// \tprotoc        v3.17.3\n// source: workflow/wfpb/wf.proto\n\npackage wfpb\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\temptypb \"google.golang.org/protobuf/types/known/emptypb\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype WorkflowData struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tData []byte `protobuf:\"bytes,1,opt,name=Data,proto3\" json:\"Data,omitempty\"`\n}\n\nfunc (x *WorkflowData) Reset() {\n\t*x = WorkflowData{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_workflow_wfpb_wf_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *WorkflowData) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*WorkflowData) ProtoMessage() {}\n\nfunc (x *WorkflowData) ProtoReflect() protoreflect.Message {\n\tmi := &file_workflow_wfpb_wf_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use WorkflowData.ProtoReflect.Descriptor instead.\nfunc (*WorkflowData) Descriptor() ([]byte, []int) {\n\treturn file_workflow_wfpb_wf_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *WorkflowData) GetData() []byte {\n\tif x != nil {\n\t\treturn x.Data\n\t}\n\treturn nil\n}\n\nvar File_workflow_wfpb_wf_proto protoreflect.FileDescriptor\n\nvar file_workflow_wfpb_wf_proto_rawDesc = []byte{\n\t0x0a, 0x16, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x2f, 0x77, 0x66, 0x70, 0x62, 0x2f,\n\t0x77, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c,\n\t0x6f, 0x77, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,\n\t0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,\n\t0x22, 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x44, 0x61, 0x74, 0x61, 0x12,\n\t0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x44,\n\t0x61, 0x74, 0x61, 0x32, 0x47, 0x0a, 0x08, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12,\n\t0x3b, 0x0a, 0x07, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x16, 0x2e, 0x77, 0x6f, 0x72,\n\t0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x44, 0x61,\n\t0x74, 0x61, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06,\n\t0x2e, 0x2f, 0x77, 0x66, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_workflow_wfpb_wf_proto_rawDescOnce sync.Once\n\tfile_workflow_wfpb_wf_proto_rawDescData = file_workflow_wfpb_wf_proto_rawDesc\n)\n\nfunc file_workflow_wfpb_wf_proto_rawDescGZIP() []byte {\n\tfile_workflow_wfpb_wf_proto_rawDescOnce.Do(func() {\n\t\tfile_workflow_wfpb_wf_proto_rawDescData = protoimpl.X.CompressGZIP(file_workflow_wfpb_wf_proto_rawDescData)\n\t})\n\treturn file_workflow_wfpb_wf_proto_rawDescData\n}\n\nvar file_workflow_wfpb_wf_proto_msgTypes = make([]protoimpl.MessageInfo, 1)\nvar file_workflow_wfpb_wf_proto_goTypes = []interface{}{\n\t(*WorkflowData)(nil),  // 0: workflow.WorkflowData\n\t(*emptypb.Empty)(nil), // 1: google.protobuf.Empty\n}\nvar file_workflow_wfpb_wf_proto_depIdxs = []int32{\n\t0, // 0: workflow.Workflow.Execute:input_type -> workflow.WorkflowData\n\t1, // 1: workflow.Workflow.Execute:output_type -> google.protobuf.Empty\n\t1, // [1:2] is the sub-list for method output_type\n\t0, // [0:1] is the sub-list for method input_type\n\t0, // [0:0] is the sub-list for extension type_name\n\t0, // [0:0] is the sub-list for extension extendee\n\t0, // [0:0] is the sub-list for field type_name\n}\n\nfunc init() { file_workflow_wfpb_wf_proto_init() }\nfunc file_workflow_wfpb_wf_proto_init() {\n\tif File_workflow_wfpb_wf_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_workflow_wfpb_wf_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*WorkflowData); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_workflow_wfpb_wf_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   1,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_workflow_wfpb_wf_proto_goTypes,\n\t\tDependencyIndexes: file_workflow_wfpb_wf_proto_depIdxs,\n\t\tMessageInfos:      file_workflow_wfpb_wf_proto_msgTypes,\n\t}.Build()\n\tFile_workflow_wfpb_wf_proto = out.File\n\tfile_workflow_wfpb_wf_proto_rawDesc = nil\n\tfile_workflow_wfpb_wf_proto_goTypes = nil\n\tfile_workflow_wfpb_wf_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "client/workflow/wfpb/wf.proto",
    "content": "syntax = \"proto3\";\n\noption go_package = \"./wfpb\";\nimport \"google/protobuf/empty.proto\";\n\npackage workflow;\n\n// The Workflow service definition.\nservice Workflow {\n  rpc Execute(WorkflowData) returns (google.protobuf.Empty) {}\n}\n\nmessage WorkflowData {\n  bytes Data = 1;\n}\n"
  },
  {
    "path": "client/workflow/wfpb/wf_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.2.0\n// - protoc             v3.17.3\n// source: workflow/wfpb/wf.proto\n\npackage wfpb\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n\temptypb \"google.golang.org/protobuf/types/known/emptypb\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\n// Requires gRPC-Go v1.32.0 or later.\nconst _ = grpc.SupportPackageIsVersion7\n\n// WorkflowClient is the client API for Workflow service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\ntype WorkflowClient interface {\n\tExecute(ctx context.Context, in *WorkflowData, opts ...grpc.CallOption) (*emptypb.Empty, error)\n}\n\ntype workflowClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewWorkflowClient(cc grpc.ClientConnInterface) WorkflowClient {\n\treturn &workflowClient{cc}\n}\n\nfunc (c *workflowClient) Execute(ctx context.Context, in *WorkflowData, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/workflow.Workflow/Execute\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// WorkflowServer is the server API for Workflow service.\n// All implementations must embed UnimplementedWorkflowServer\n// for forward compatibility\ntype WorkflowServer interface {\n\tExecute(context.Context, *WorkflowData) (*emptypb.Empty, error)\n\tmustEmbedUnimplementedWorkflowServer()\n}\n\n// UnimplementedWorkflowServer must be embedded to have forward compatible implementations.\ntype UnimplementedWorkflowServer struct {\n}\n\nfunc (UnimplementedWorkflowServer) Execute(context.Context, *WorkflowData) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method Execute not implemented\")\n}\nfunc (UnimplementedWorkflowServer) mustEmbedUnimplementedWorkflowServer() {}\n\n// UnsafeWorkflowServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to WorkflowServer will\n// result in compilation errors.\ntype UnsafeWorkflowServer interface {\n\tmustEmbedUnimplementedWorkflowServer()\n}\n\nfunc RegisterWorkflowServer(s grpc.ServiceRegistrar, srv WorkflowServer) {\n\ts.RegisterService(&Workflow_ServiceDesc, srv)\n}\n\nfunc _Workflow_Execute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(WorkflowData)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(WorkflowServer).Execute(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/workflow.Workflow/Execute\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(WorkflowServer).Execute(ctx, req.(*WorkflowData))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// Workflow_ServiceDesc is the grpc.ServiceDesc for Workflow service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar Workflow_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"workflow.Workflow\",\n\tHandlerType: (*WorkflowServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"Execute\",\n\t\t\tHandler:    _Workflow_Execute_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"workflow/wfpb/wf.proto\",\n}\n"
  },
  {
    "path": "client/workflow/workflow.go",
    "content": "package workflow\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\t\"github.com/dtm-labs/dtm/client/workflow/wfpb\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/go-resty/resty/v2\"\n\t\"google.golang.org/grpc\"\n)\n\n// InitHTTP will init Workflow engine to use http\n// param httpDtm specify the dtm address\n// param callback specify the url for dtm to callback if a workflow timeout\nfunc InitHTTP(httpDtm string, callback string) {\n\tdefaultFac.protocol = dtmimp.ProtocolHTTP\n\tdefaultFac.httpDtm = httpDtm\n\tdefaultFac.httpCallback = callback\n}\n\n// InitGrpc will init Workflow engine to use grpc\n// param dtm specify the dtm address\n// param clientHost specify the client host for dtm to callback if a workflow timeout\n// param grpcServer specify the grpc server\nfunc InitGrpc(grpcDtm string, clientHost string, grpcServer *grpc.Server) {\n\tdefaultFac.protocol = dtmimp.ProtocolGRPC\n\tdefaultFac.grpcDtm = grpcDtm\n\twfpb.RegisterWorkflowServer(grpcServer, &workflowServer{})\n\tdefaultFac.grpcCallback = clientHost + \"/workflow.Workflow/Execute\"\n}\n\n// SetProtocolForTest change protocol directly. only used by test\nfunc SetProtocolForTest(protocol string) {\n\tdefaultFac.protocol = protocol\n}\n\n// Register will register a workflow with the specified name\nfunc Register(name string, handler WfFunc, custom ...func(wf *Workflow)) error {\n\treturn defaultFac.register(name, func(wf *Workflow, data []byte) ([]byte, error) {\n\t\treturn nil, handler(wf, data)\n\t}, custom...)\n}\n\n// Register2 is the same as Register, but workflow func can return result\nfunc Register2(name string, handler WfFunc2, custom ...func(wf *Workflow)) error {\n\treturn defaultFac.register(name, handler, custom...)\n}\n\n// ExecuteCtx will execute a workflow with the gid and specified params\n// if the workflow with the gid does not exist, then create a new workflow and execute it\n// if the workflow with the gid exists, resume to execute it\nfunc ExecuteCtx(ctx context.Context, name string, gid string, data []byte) ([]byte, error) {\n\treturn defaultFac.execute(ctx, name, gid, data)\n}\n\n// Execute is the same as ExecuteCtx, but with context.Background\n// Deprecated: use ExecuteCtx instead\nfunc Execute(name string, gid string, data []byte) error {\n\t_, err := ExecuteCtx(context.Background(), name, gid, data)\n\treturn err\n}\n\n// Execute2 is the same as Execute, but workflow func can return result\n// Deprecated: use ExecuteCtx instead\nfunc Execute2(name string, gid string, data []byte) ([]byte, error) {\n\treturn ExecuteCtx(context.Background(), name, gid, data)\n}\n\n// ExecuteByQS is like Execute, but name and gid will be obtained from qs\n// Deprecated: use ExecuteCtx instead\nfunc ExecuteByQS(qs url.Values, body []byte) error {\n\tname := qs.Get(\"op\")\n\tgid := qs.Get(\"gid\")\n\t_, err := ExecuteCtx(context.Background(), name, gid, body)\n\treturn err\n}\n\n// Options is for specifying workflow options\ntype Options struct {\n\n\t// Default == HTTPResp2DtmError : Code 409 => ErrFailure; Code 425 => ErrOngoing\n\tHTTPResp2DtmError func(*http.Response) ([]byte, error)\n\n\t// Default == GrpcError2DtmError: Code Aborted => ErrFailure; Code FailedPrecondition => ErrOngoing\n\tGRPCError2DtmError func(error) error\n\n\t// This Option specify whether a branch returning ErrFailure should be compensated on rollback.\n\t// for most idempotent branches, no compensation is needed.\n\t// But for a timeout request, the caller cannot know where the request is successful, so the compensation should be called\n\tCompensateErrorBranch bool\n}\n\n// Workflow is the type for a workflow\ntype Workflow struct {\n\t// The name of the workflow\n\tName    string\n\tOptions Options\n\t*dtmimp.TransBase\n\tworkflowImp\n}\n\ntype wfItem struct {\n\tfn     WfFunc2\n\tcustom []func(*Workflow)\n}\n\n// WfFunc is the type for workflow function\ntype WfFunc func(wf *Workflow, data []byte) error\n\n// WfFunc2 is the type for workflow function with return value\ntype WfFunc2 func(wf *Workflow, data []byte) ([]byte, error)\n\n// WfPhase2Func is the type for phase 2 function\n// param bb is a BranchBarrier, which is introduced by http://d.dtm.pub/practice/barrier.html\ntype WfPhase2Func func(bb *dtmcli.BranchBarrier) error\n\n// NewRequest return a new resty request, whose progress will be recorded\nfunc (wf *Workflow) NewRequest() *resty.Request {\n\treturn wf.restyClient.R().SetContext(wf.Context)\n}\n\n// NewBranch will start a new branch transaction\nfunc (wf *Workflow) NewBranch() *Workflow {\n\tdtmimp.PanicIf(wf.currentOp != dtmimp.OpAction, fmt.Errorf(\"should not call NewBranch() in Branch callbacks\"))\n\twf.idGen.NewSubBranchID()\n\twf.currentBranch = wf.idGen.CurrentSubBranchID()\n\twf.currentActionAdded = false\n\twf.currentCommitAdded = false\n\twf.currentRollbackAdded = false\n\treturn wf\n}\n\n// NewBranchCtx will call NewBranch and return a workflow context\nfunc (wf *Workflow) NewBranchCtx() context.Context {\n\treturn wf.NewBranch().Context\n}\n\n// OnRollback will set the callback for current branch when rollback happen.\n// If you are writing a saga transaction, then you should  write the compensation here\n// If you are writing a tcc transaction, then you should write the cancel operation here\nfunc (wf *Workflow) OnRollback(compensate WfPhase2Func) *Workflow {\n\tbranchID := wf.currentBranch\n\tdtmimp.PanicIf(wf.currentRollbackAdded, fmt.Errorf(\"one branch can only add one rollback callback\"))\n\twf.currentRollbackAdded = true\n\twf.failedOps = append(wf.failedOps, workflowPhase2Item{\n\t\tbranchID: branchID,\n\t\top:       dtmimp.OpCommit,\n\t\tfn:       compensate,\n\t})\n\treturn wf\n}\n\n// OnCommit will will set the callback for current branch when commit happen.\n// If you are writing a tcc transaction, then you should write the confirm operation here\nfunc (wf *Workflow) OnCommit(fn WfPhase2Func) *Workflow {\n\tbranchID := wf.currentBranch\n\tdtmimp.PanicIf(wf.currentCommitAdded, fmt.Errorf(\"one branch can only add one commit callback\"))\n\twf.currentCommitAdded = true\n\twf.succeededOps = append(wf.succeededOps, workflowPhase2Item{\n\t\tbranchID: branchID,\n\t\top:       dtmimp.OpCommit,\n\t\tfn:       fn,\n\t})\n\treturn wf\n}\n\n// OnFinish will both set the callback for OnCommit and OnRollback\nfunc (wf *Workflow) OnFinish(fn func(bb *dtmcli.BranchBarrier, isRollback bool) error) *Workflow {\n\treturn wf.OnCommit(func(bb *dtmcli.BranchBarrier) error {\n\t\treturn fn(bb, false)\n\t}).OnRollback(func(bb *dtmcli.BranchBarrier) error {\n\t\treturn fn(bb, true)\n\t})\n}\n\n// Do will do an action which will be recored\nfunc (wf *Workflow) Do(fn func(bb *dtmcli.BranchBarrier) ([]byte, error)) ([]byte, error) {\n\tres := wf.recordedDo(func(bb *dtmcli.BranchBarrier) *stepResult {\n\t\tr, e := fn(bb)\n\t\treturn wf.stepResultFromLocal(r, e)\n\t})\n\treturn wf.stepResultToLocal(res)\n}\n\n// DoXa will begin a local xa transaction\n// after the return of workflow function, xa commit/rollback will be called\nfunc (wf *Workflow) DoXa(dbConf dtmcli.DBConf, fn func(db *sql.DB) ([]byte, error)) ([]byte, error) {\n\tbranchID := wf.currentBranch\n\tres := wf.recordedDo(func(bb *dtmcli.BranchBarrier) *stepResult {\n\t\tsBusi := \"business\"\n\t\tk := bb.BranchID + \"-\" + sBusi\n\t\tif wf.progresses[k] != nil {\n\t\t\treturn &stepResult{\n\t\t\t\tError: fmt.Errorf(\"error occur at prepare, not resumable, to rollback. %w\", dtmcli.ErrFailure),\n\t\t\t}\n\t\t}\n\t\tsr := &stepResult{}\n\t\twf.TransBase.BranchID = branchID\n\t\twf.TransBase.Op = sBusi\n\t\terr := dtmimp.XaHandleLocalTrans(wf.TransBase, dbConf, func(d *sql.DB) error {\n\t\t\tr, e := fn(d)\n\t\t\tsr.Data = r\n\t\t\tif e == nil {\n\t\t\t\te = wf.saveResult(branchID, sBusi, &stepResult{Status: dtmcli.StatusSucceed})\n\t\t\t}\n\t\t\treturn e\n\t\t})\n\t\tsr.Error = err\n\t\tsr.Status = wfErrorToStatus(err)\n\t\treturn sr\n\t})\n\tphase2 := func(bb *dtmcli.BranchBarrier) error {\n\t\treturn dtmimp.XaHandlePhase2(bb.Gid, dbConf, bb.BranchID, bb.Op)\n\t}\n\twf.succeededOps = append(wf.succeededOps, workflowPhase2Item{\n\t\tbranchID: branchID,\n\t\top:       dtmimp.OpCommit,\n\t\tfn:       phase2,\n\t})\n\twf.failedOps = append(wf.failedOps, workflowPhase2Item{\n\t\tbranchID: branchID,\n\t\top:       dtmimp.OpRollback,\n\t\tfn:       phase2,\n\t})\n\treturn res.Data, res.Error\n}\n\n// Interceptor is the middleware for workflow to capture grpc call result\nfunc Interceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {\n\tlogger.Debugf(\"grpc client calling: %s%s %v\", cc.Target(), method, dtmimp.MustMarshalString(req))\n\twfVal := ctx.Value(wfMeta{})\n\tif wfVal == nil {\n\t\treturn invoker(ctx, method, req, reply, cc, opts...)\n\t}\n\twf, _ := wfVal.(*Workflow)\n\torigin := func() error {\n\t\tctx1 := dtmgimp.TransInfo2Ctx(ctx, wf.Gid, wf.TransType, wf.currentBranch, wf.currentOp, wf.Dtm)\n\t\terr := invoker(ctx1, method, req, reply, cc, opts...)\n\t\tres := fmt.Sprintf(\"grpc client called: %s%s %s result: %s err: %v\",\n\t\t\tcc.Target(), method, dtmimp.MustMarshalString(req), dtmimp.MustMarshalString(reply), err)\n\t\tif err != nil {\n\t\t\tlogger.Errorf(\"%s\", res)\n\t\t} else {\n\t\t\tlogger.Debugf(\"%s\", res)\n\t\t}\n\t\treturn err\n\t}\n\tif wf.currentOp != dtmimp.OpAction {\n\t\treturn origin()\n\t}\n\tsr := wf.recordedDo(func(bb *dtmcli.BranchBarrier) *stepResult {\n\t\terr := origin()\n\t\treturn wf.stepResultFromGrpc(reply, err)\n\t})\n\treturn wf.stepResultToGrpc(sr, reply)\n}\n"
  },
  {
    "path": "client/workflow/workflow_test.go",
    "content": "package workflow\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestAbnormal(t *testing.T) {\n\tfname := dtmimp.GetFuncName()\n\t_, err := defaultFac.execute(context.Background(), fname, fname, nil)\n\tassert.Error(t, err)\n\n\terr = defaultFac.register(fname, func(wf *Workflow, data []byte) ([]byte, error) { return nil, nil })\n\tassert.Nil(t, err)\n\terr = defaultFac.register(fname, nil)\n\tassert.Error(t, err)\n\n\tws := &workflowServer{}\n\t_, err = ws.Execute(context.Background(), nil)\n\tassert.Contains(t, err.Error(), \"call workflow.InitGrpc first\")\n}\n"
  },
  {
    "path": "conf.sample.yml",
    "content": "#####################################################################\n### dtm can be run without any config.\n### all config in this file is optional. the default value is as specified in each line\n### all configs can be specified from env. for example:\n### MicroService.EndPoint => MICRO_SERVICE_END_POINT\n#####################################################################\n\n# Store: # specify which engine to store trans status\n#   Driver: 'mysql'\n#   Host: 'localhost'\n#   User: 'root'\n#   Password: ''\n#   Port: 3306\n#   Db: 'dtm'\n\n#   Driver: 'boltdb' # default store engine\n\n#   Driver: 'redis'\n#   Host: 'localhost' # host1:port1,host2:port2 for cluster connection\n#   User: ''\n#   Password: ''\n#   Port: 6379 # required but won't be used for cluster connection\n\n#   Driver: 'postgres'\n#   Host: 'localhost'\n#   User: 'postgres'\n#   Password: 'mysecretpassword'\n#   Port: '5432'\n#   Db: 'postgres'\n#   Schema: 'public' # default value is 'public'\n\n### following config is for only Driver postgres/mysql\n#   MaxOpenConns: 500\n#   MaxIdleConns: 500\n#   ConnMaxLifeTime: 5 # default value is 5 (minutes)\n\n### flollowing config is only for some Driver\n#   DataExpire: 604800 # Trans data will expire in 7 days. only for redis/boltdb.\n#   FinishedDataExpire: 86400 # finished Trans data will expire in 1 days. only for redis.\n#   RedisPrefix: '{a}' # default value is '{a}'. Redis storage prefix. store data to only one slot in cluster\n\n# MicroService: # gRPC/HTTP based microservice config\n#   Driver: 'dtm-driver-gozero' # name of the driver to handle register/discover\n#   Target: 'etcd://localhost:2379/dtmservice' # register dtm server to this url\n#   EndPoint: 'localhost:36790'\n\n### the unit of following configurations is second\n# TransCronInterval: 3 # the interval to poll unfinished global transaction for every dtm process\n# TimeoutToFail: 35 # timeout for XA, TCC to fail. saga's timeout default to infinite, which can be overwritten in saga options\n# RetryInterval: 10 # the subtrans branch will be retried after this interval\n# RequestTimeout: 3 # the timeout of HTTP/gRPC request in dtm\n\n# LogLevel: 'info'              # default: info. can be debug|info|warn|error\n# Log:\n#    Outputs: 'stderr'           # default: stderr, split by \",\", you can append files to Outputs if need. example:'stderr,/tmp/test.log'\n#    RotationEnable: 0           # default: 0\n#    RotationConfigJSON: '{}'    # example: '{\"maxsize\": 100, \"maxage\": 0, \"maxbackups\": 0, \"localtime\": false, \"compress\": false}'\n#\n# HttpPort: 36789\n# GrpcPort: 36790\n# JsonRpcPort: 36791\n\n### advanced options\n# UpdateBranchAsyncGoroutineNum: 1 # num of async goroutine to update branch status\n# TimeZoneOffset: '' #default '' using system default. '+8': Asia/Shanghai; '0': GMT\n# AdminBasePath: '' #default '' set admin access base path\n\n# ConfigUpdateInterval: 10   # the interval to update configuration in memory such as topics map... (seconds)\n# TimeZoneOffset: '' # default '' using system default. '+8': Asia/Shanghai; '0': GMT\n# AlertRetryLimit: 3 # default 3; if a transaction branch has been retried 3 times, the AlertHook will be called\n# AlertWebHook: '' # default ''; sample: 'http://localhost:8080/dtm-hook'. this hook will be called like this:\n## curl -H \"Content-Type: application/json\" -d '{\"gid\":\"xxxx\",\"status\":\"submitted\",\"retry_count\":3}' http://localhost:8080/dtm-hook\n\n# GrpcBalancer: 'round_robin' # default round_robin,For more information about service configs, see:https://github.com/grpc/grpc/blob/master/doc/service_config.md\n# GrpcServiceConfig: '{\"loadBalancingConfig\": [{\"round_robin\":{}}]}' # default gRPC service config for client connections\n"
  },
  {
    "path": "dtmsvr/api.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmsvr\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage\"\n\t\"github.com/dtm-labs/logger\"\n)\n\n// Version store the passin version for dtm server\nvar Version = \"\"\n\nfunc svcSubmit(t *TransGlobal) interface{} {\n\tif t.TransType == \"workflow\" {\n\t\tt.Status = dtmcli.StatusPrepared\n\t\tt.changeStatus(t.ReqExtra[\"status\"], withRollbackReason(t.ReqExtra[\"rollback_reason\"]), withResult(t.ReqExtra[\"result\"]))\n\t\treturn nil\n\t}\n\tt.Status = dtmcli.StatusSubmitted\n\tbranches, err := t.saveNew()\n\n\tif err == storage.ErrUniqueConflict {\n\t\tdbt := GetTransGlobal(t.Gid)\n\t\tif dbt.Status == dtmcli.StatusPrepared {\n\t\t\tdbt.changeStatus(t.Status)\n\t\t\tbranches = GetStore().FindBranches(t.Gid)\n\t\t} else if dbt.Status != dtmcli.StatusSubmitted {\n\t\t\treturn fmt.Errorf(\"current status '%s', cannot sumbmit. %w\", dbt.Status, dtmcli.ErrFailure)\n\t\t}\n\t} else if err != nil {\n\t\treturn err\n\t}\n\treturn t.Process(branches)\n}\n\nfunc svcPrepare(t *TransGlobal) interface{} {\n\tt.Status = dtmcli.StatusPrepared\n\t_, err := t.saveNew()\n\tif err == storage.ErrUniqueConflict {\n\t\tdbt := GetTransGlobal(t.Gid)\n\t\tif dbt.Status != dtmcli.StatusPrepared {\n\t\t\treturn fmt.Errorf(\"current status '%s', cannot prepare. %w\", dbt.Status, dtmcli.ErrFailure)\n\t\t}\n\t\treturn nil\n\t}\n\treturn err\n}\n\nfunc svcPrepareWorkflow(t *TransGlobal) (*storage.TransGlobalStore, []TransBranch, error) {\n\tt.Status = dtmcli.StatusPrepared\n\t_, err := t.saveNew()\n\tif err == storage.ErrUniqueConflict { // transaction exists, query the branches\n\t\tst := GetStore()\n\t\treturn st.FindTransGlobalStore(t.Gid), st.FindBranches(t.Gid), nil\n\t}\n\treturn &t.TransGlobalStore, []TransBranch{}, err\n}\n\nfunc svcAbort(t *TransGlobal) interface{} {\n\tdbt := GetTransGlobal(t.Gid)\n\tif dbt.TransType == \"msg\" && dbt.Status == dtmcli.StatusPrepared {\n\t\tdbt.changeStatus(dtmcli.StatusFailed)\n\t\treturn nil\n\t}\n\tif t.TransType != \"xa\" && t.TransType != \"tcc\" || dbt.Status != dtmcli.StatusPrepared && dbt.Status != dtmcli.StatusAborting {\n\t\treturn fmt.Errorf(\"trans type: '%s' current status '%s', cannot abort. %w\", dbt.TransType, dbt.Status, dtmcli.ErrFailure)\n\t}\n\tdbt.changeStatus(dtmcli.StatusAborting, withRollbackReason(t.RollbackReason))\n\tbranches := GetStore().FindBranches(t.Gid)\n\treturn dbt.Process(branches)\n}\n\nfunc svcForceStop(t *TransGlobal) interface{} {\n\tdbt := GetTransGlobal(t.Gid)\n\tif dbt.Status == dtmcli.StatusSucceed || dbt.Status == dtmcli.StatusFailed {\n\t\treturn fmt.Errorf(\"global transaction force stop error. status: %s. error: %w\", dbt.Status, dtmcli.ErrFailure)\n\t}\n\tdbt.changeStatus(dtmcli.StatusFailed)\n\treturn nil\n}\n\nfunc svcResetNextCronTime(t *TransGlobal) error {\n\tdbt := GetTransGlobal(t.Gid)\n\treturn dbt.resetNextCronTime()\n}\n\nfunc svcRegisterBranch(transType string, branch *TransBranch, data map[string]string) error {\n\tbranches := []TransBranch{*branch, *branch}\n\tif transType == \"tcc\" {\n\t\tfor i, b := range []string{dtmimp.OpCancel, dtmimp.OpConfirm} {\n\t\t\tbranches[i].Op = b\n\t\t\tbranches[i].URL = data[b]\n\t\t}\n\t} else if transType == \"xa\" {\n\t\tbranches[0].Op = dtmimp.OpRollback\n\t\tbranches[0].URL = data[\"url\"]\n\t\tbranches[1].Op = dtmimp.OpCommit\n\t\tbranches[1].URL = data[\"url\"]\n\t} else if transType == \"workflow\" {\n\t\tif data[\"sync\"] == \"\" && conf.UpdateBranchSync == 0 {\n\t\t\tnow := time.Now()\n\t\t\tupdateBranchAsyncChan <- branchStatus{\n\t\t\t\tgid:        branch.Gid,\n\t\t\t\tbranchID:   branch.BranchID,\n\t\t\t\top:         data[\"op\"],\n\t\t\t\tstatus:     data[\"status\"],\n\t\t\t\tfinishTime: &now,\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\tbranches = []TransBranch{*branch}\n\t\tbranches[0].Status = data[\"status\"]\n\t\tbranches[0].Op = data[\"op\"]\n\t} else {\n\t\treturn fmt.Errorf(\"unknow trans type: %s\", transType)\n\t}\n\n\terr := dtmimp.CatchP(func() {\n\t\tGetStore().LockGlobalSaveBranches(branch.Gid, dtmcli.StatusPrepared, branches, -1)\n\t})\n\tif err == storage.ErrNotFound {\n\t\tmsg := fmt.Sprintf(\"no trans with gid: %s status: %s found\", branch.Gid, dtmcli.StatusPrepared)\n\t\tlogger.Errorf(msg)\n\t\treturn dtmcli.ErrorMessage2Error(msg, dtmcli.ErrFailure)\n\t}\n\tlogger.Infof(\"LockGlobalSaveBranches result: %v: gid: %s old status: %s branches: %s\",\n\t\terr, branch.Gid, dtmcli.StatusPrepared, dtmimp.MustMarshalString(branches))\n\treturn err\n}\n"
  },
  {
    "path": "dtmsvr/api_grpc.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmsvr\n\nimport (\n\t\"context\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc\"\n\tpb \"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgpb\"\n\t\"google.golang.org/protobuf/types/known/emptypb\"\n)\n\n// dtmServer is used to implement dtmgimp.DtmServer.\ntype dtmServer struct {\n\tpb.UnimplementedDtmServer\n}\n\nfunc (s *dtmServer) NewGid(ctx context.Context, in *emptypb.Empty) (*pb.DtmGidReply, error) {\n\treturn &pb.DtmGidReply{Gid: GenGid()}, nil\n}\n\nfunc (s *dtmServer) Submit(ctx context.Context, in *pb.DtmRequest) (*emptypb.Empty, error) {\n\tr := svcSubmit(TransFromDtmRequest(ctx, in))\n\treturn &emptypb.Empty{}, dtmgrpc.DtmError2GrpcError(r)\n}\n\nfunc (s *dtmServer) Prepare(ctx context.Context, in *pb.DtmRequest) (*emptypb.Empty, error) {\n\tr := svcPrepare(TransFromDtmRequest(ctx, in))\n\treturn &emptypb.Empty{}, dtmgrpc.DtmError2GrpcError(r)\n}\n\nfunc (s *dtmServer) Abort(ctx context.Context, in *pb.DtmRequest) (*emptypb.Empty, error) {\n\tr := svcAbort(TransFromDtmRequest(ctx, in))\n\treturn &emptypb.Empty{}, dtmgrpc.DtmError2GrpcError(r)\n}\n\nfunc (s *dtmServer) RegisterBranch(ctx context.Context, in *pb.DtmBranchRequest) (*emptypb.Empty, error) {\n\tr := svcRegisterBranch(in.TransType, &TransBranch{\n\t\tGid:      in.Gid,\n\t\tBranchID: in.BranchID,\n\t\tStatus:   dtmcli.StatusPrepared,\n\t\tBinData:  in.BusiPayload,\n\t}, in.Data)\n\treturn &emptypb.Empty{}, dtmgrpc.DtmError2GrpcError(r)\n}\n\nfunc (s *dtmServer) PrepareWorkflow(ctx context.Context, in *pb.DtmRequest) (*pb.DtmProgressesReply, error) {\n\ttrans, branches, err := svcPrepareWorkflow(TransFromDtmRequest(ctx, in))\n\treply := &pb.DtmProgressesReply{\n\t\tTransaction: &pb.DtmTransaction{\n\t\t\tGid:            trans.Gid,\n\t\t\tStatus:         trans.Status,\n\t\t\tRollbackReason: trans.RollbackReason,\n\t\t},\n\t\tProgresses: []*pb.DtmProgress{},\n\t}\n\tfor _, b := range branches {\n\t\treply.Progresses = append(reply.Progresses, &pb.DtmProgress{\n\t\t\tStatus:   b.Status,\n\t\t\tBranchID: b.BranchID,\n\t\t\tOp:       b.Op,\n\t\t\tBinData:  b.BinData,\n\t\t})\n\t}\n\treturn reply, dtmgrpc.DtmError2GrpcError(err)\n}\n\nfunc (s *dtmServer) Subscribe(ctx context.Context, in *pb.DtmTopicRequest) (*emptypb.Empty, error) {\n\treturn &emptypb.Empty{}, dtmgrpc.DtmError2GrpcError(Subscribe(in.Topic, in.URL, in.Remark))\n}\n\nfunc (s *dtmServer) Unsubscribe(ctx context.Context, in *pb.DtmTopicRequest) (*emptypb.Empty, error) {\n\treturn &emptypb.Empty{}, dtmgrpc.DtmError2GrpcError(Unsubscribe(in.Topic, in.URL))\n}\n\nfunc (s *dtmServer) DeleteTopic(ctx context.Context, in *pb.DtmTopicRequest) (*emptypb.Empty, error) {\n\treturn &emptypb.Empty{}, dtmgrpc.DtmError2GrpcError(GetStore().DeleteKV(topicsCat, in.Topic))\n}\n"
  },
  {
    "path": "dtmsvr/api_http.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmsvr\n\nimport (\n\t\"errors\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/prometheus/client_golang/prometheus/promhttp\"\n)\n\nfunc addRoute(engine *gin.Engine) {\n\tengine.GET(\"/api/dtmsvr/version\", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {\n\t\treturn gin.H{\"version\": Version}\n\t}))\n\tengine.GET(\"/api/dtmsvr/newGid\", dtmutil.WrapHandler2(newGid))\n\tengine.POST(\"/api/dtmsvr/prepare\", dtmutil.WrapHandler2(prepare))\n\tengine.POST(\"/api/dtmsvr/submit\", dtmutil.WrapHandler2(submit))\n\tengine.POST(\"/api/dtmsvr/abort\", dtmutil.WrapHandler2(abort))\n\tengine.POST(\"/api/dtmsvr/forceStop\", dtmutil.WrapHandler2(forceStop)) // change global status to failed can stop trigger (Use with caution in production environment)\n\tengine.POST(\"/api/dtmsvr/registerBranch\", dtmutil.WrapHandler2(registerBranch))\n\tengine.POST(\"/api/dtmsvr/registerXaBranch\", dtmutil.WrapHandler2(registerBranch))  // compatible for old sdk\n\tengine.POST(\"/api/dtmsvr/registerTccBranch\", dtmutil.WrapHandler2(registerBranch)) // compatible for old sdk\n\tengine.POST(\"/api/dtmsvr/prepareWorkflow\", dtmutil.WrapHandler2(prepareWorkflow))\n\tengine.GET(\"/api/dtmsvr/query\", dtmutil.WrapHandler2(query))\n\tengine.GET(\"/api/dtmsvr/all\", dtmutil.WrapHandler2(all))\n\tengine.GET(\"/api/dtmsvr/resetCronTime\", dtmutil.WrapHandler2(resetCronTime))\n\tengine.GET(\"/api/dtmsvr/subscribe\", dtmutil.WrapHandler2(subscribe))\n\tengine.GET(\"/api/dtmsvr/unsubscribe\", dtmutil.WrapHandler2(unsubscribe))\n\tengine.DELETE(\"/api/dtmsvr/topic/:topicName\", dtmutil.WrapHandler2(deleteTopic))\n\tengine.GET(\"/api/dtmsvr/scanKV\", dtmutil.WrapHandler2(scanKV))\n\tengine.GET(\"/api/dtmsvr/queryKV\", dtmutil.WrapHandler2(queryKV))\n\tengine.POST(\"/api/dtmsvr/resetNextCronTime\", dtmutil.WrapHandler2(resetNextCronTime)) // one global trans only\n\n\t// add prometheus exporter\n\th := promhttp.Handler()\n\tengine.GET(\"/api/metrics\", func(c *gin.Context) {\n\t\th.ServeHTTP(c.Writer, c.Request)\n\t})\n}\n\n// NOTE: unique in storage, can customize the generation rules instead of using server-side generation, it will help with the tracking\nfunc newGid(c *gin.Context) interface{} {\n\treturn map[string]interface{}{\"gid\": GenGid(), \"dtm_result\": dtmcli.ResultSuccess}\n}\n\nfunc prepare(c *gin.Context) interface{} {\n\treturn svcPrepare(TransFromContext(c))\n}\n\nfunc submit(c *gin.Context) interface{} {\n\treturn svcSubmit(TransFromContext(c))\n}\n\nfunc abort(c *gin.Context) interface{} {\n\treturn svcAbort(TransFromContext(c))\n}\n\nfunc forceStop(c *gin.Context) interface{} {\n\treturn svcForceStop(TransFromContext(c))\n}\n\nfunc resetNextCronTime(c *gin.Context) interface{} {\n\treturn svcResetNextCronTime(TransFromContext(c))\n}\n\nfunc registerBranch(c *gin.Context) interface{} {\n\tdata := map[string]string{}\n\terr := c.BindJSON(&data)\n\te2p(err)\n\tbranch := TransBranch{\n\t\tGid:      dtmimp.Escape(data[\"gid\"]),\n\t\tBranchID: data[\"branch_id\"],\n\t\tStatus:   dtmcli.StatusPrepared,\n\t\tBinData:  []byte(data[\"data\"]),\n\t}\n\treturn svcRegisterBranch(data[\"trans_type\"], &branch, data)\n}\n\nfunc query(c *gin.Context) interface{} {\n\tgid := c.Query(\"gid\")\n\tif gid == \"\" {\n\t\treturn errors.New(\"no gid specified\")\n\t}\n\ttrans := GetStore().FindTransGlobalStore(gid)\n\tbranches := GetStore().FindBranches(gid)\n\treturn map[string]interface{}{\"transaction\": trans, \"branches\": branches}\n}\n\nfunc prepareWorkflow(c *gin.Context) interface{} {\n\ttrans, branches, err := svcPrepareWorkflow(TransFromContext(c))\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn map[string]interface{}{\"transaction\": trans, \"progresses\": branches}\n}\n\nfunc all(c *gin.Context) interface{} {\n\tgid := c.Query(\"gid\")\n\tposition := c.Query(\"position\")\n\tsLimit := dtmimp.OrString(c.Query(\"limit\"), \"100\")\n\n\tvar globals interface{}\n\tif len(gid) > 0 {\n\t\tfind := GetStore().FindTransGlobalStore(gid)\n\t\tif find != nil {\n\t\t\tglobals = []interface{}{*find}\n\t\t}\n\t} else {\n\t\tcondition := storage.TransGlobalScanCondition{\n\t\t\tStatus:          c.Query(\"status\"),\n\t\t\tTransType:       c.Query(\"transType\"),\n\t\t\tCreateTimeStart: stringTotime(c.Query(\"createTimeStart\")),\n\t\t\tCreateTimeEnd:   stringTotime(c.Query(\"createTimeEnd\")),\n\t\t}\n\t\tglobals = GetStore().ScanTransGlobalStores(&position, int64(dtmimp.MustAtoi(sLimit)), condition)\n\t}\n\treturn map[string]interface{}{\"transactions\": globals, \"next_position\": position}\n}\n\nfunc stringTotime(timeStr string) time.Time {\n\tif timeStr == \"\" {\n\t\treturn time.Time{}\n\t}\n\treturn time.Unix(int64(dtmimp.MustAtoi(timeStr))/1000, 0)\n}\n\n// unfinished transactions need to be retried as soon as possible after business downtime is recovered\nfunc resetCronTime(c *gin.Context) interface{} {\n\tsTimeoutSecond := dtmimp.OrString(c.Query(\"timeout\"), strconv.FormatInt(3*conf.TimeoutToFail, 10))\n\tsLimit := dtmimp.OrString(c.Query(\"limit\"), \"100\")\n\ttimeout := time.Duration(dtmimp.MustAtoi(sTimeoutSecond)) * time.Second\n\n\tsucceedCount, hasRemaining, err := GetStore().ResetCronTime(timeout, int64(dtmimp.MustAtoi(sLimit)))\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn map[string]interface{}{\"has_remaining\": hasRemaining, \"succeed_count\": succeedCount}\n}\n\nfunc scanKV(c *gin.Context) interface{} {\n\tcat := c.DefaultQuery(\"cat\", \"\")\n\tposition := c.Query(\"position\")\n\tsLimit := dtmimp.OrString(c.Query(\"limit\"), \"100\")\n\tkv := GetStore().ScanKV(cat, &position, int64(dtmimp.MustAtoi(sLimit)))\n\treturn map[string]interface{}{\"kv\": kv, \"next_position\": position}\n}\n\nfunc queryKV(c *gin.Context) interface{} {\n\tcat := c.DefaultQuery(\"cat\", \"\")\n\tkey := c.DefaultQuery(\"key\", \"\")\n\n\tkv := GetStore().FindKV(cat, key)\n\treturn map[string]interface{}{\"kv\": kv}\n}\n\nfunc subscribe(c *gin.Context) interface{} {\n\ttopic := c.Query(\"topic\")\n\turl := c.Query(\"url\")\n\tremark := c.Query(\"remark\")\n\n\treturn Subscribe(topic, url, remark)\n}\n\nfunc unsubscribe(c *gin.Context) interface{} {\n\ttopic := c.Query(\"topic\")\n\turl := c.Query(\"url\")\n\n\treturn Unsubscribe(topic, url)\n}\n\nfunc deleteTopic(c *gin.Context) interface{} {\n\ttopic := c.Param(\"topicName\")\n\tif topic == \"\" {\n\t\treturn errors.New(\"empty topic\")\n\t}\n\n\treturn GetStore().DeleteKV(topicsCat, topic)\n}\n"
  },
  {
    "path": "dtmsvr/api_json_rpc.go",
    "content": "package dtmsvr\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype jrpcReq struct {\n\tMethod  string      `json:\"method\"`\n\tJsonrpc string      `json:\"jsonrpc\"`\n\tParams  interface{} `json:\"params\"`\n\tID      string      `json:\"id\"`\n}\n\nfunc addJrpcRouter(engine *gin.Engine) {\n\ttype jrpcFunc = func(interface{}) interface{}\n\thandlers := map[string]jrpcFunc{\n\t\t\"newGid\":         jrpcNewGid,\n\t\t\"prepare\":        jrpcPrepare,\n\t\t\"submit\":         jrpcSubmit,\n\t\t\"abort\":          jrpcAbort,\n\t\t\"registerBranch\": jrpcRegisterBranch,\n\t}\n\tengine.POST(\"/api/json-rpc\", func(c *gin.Context) {\n\t\tbegan := time.Now()\n\t\tvar err error\n\t\tvar req jrpcReq\n\t\tvar jerr map[string]interface{}\n\t\tr := func() interface{} {\n\t\t\tdefer dtmimp.P2E(&err)\n\t\t\terr2 := c.BindJSON(&req)\n\t\t\tif err2 != nil {\n\t\t\t\tjerr = map[string]interface{}{\n\t\t\t\t\t\"code\":    -32700,\n\t\t\t\t\t\"message\": fmt.Sprintf(\"Parse json error: %s\", err2.Error()),\n\t\t\t\t}\n\t\t\t} else if req.ID == \"\" || req.Jsonrpc != \"2.0\" {\n\t\t\t\tjerr = map[string]interface{}{\n\t\t\t\t\t\"code\":    -32600,\n\t\t\t\t\t\"message\": fmt.Sprintf(\"Bad json request: %s\", dtmimp.MustMarshalString(req)),\n\t\t\t\t}\n\t\t\t} else if handlers[req.Method] == nil {\n\t\t\t\tjerr = map[string]interface{}{\n\t\t\t\t\t\"code\":    -32601,\n\t\t\t\t\t\"message\": fmt.Sprintf(\"Method not found: %s\", req.Method),\n\t\t\t\t}\n\t\t\t} else if handlers[req.Method] != nil {\n\t\t\t\treturn handlers[req.Method](req.Params)\n\t\t\t}\n\t\t\treturn nil\n\t\t}()\n\n\t\t// error maybe returned in r, assign it to err\n\t\tif ne, ok := r.(error); ok && err == nil {\n\t\t\terr = ne\n\t\t}\n\n\t\tif err != nil {\n\t\t\tif errors.Is(err, dtmcli.ErrFailure) {\n\t\t\t\tjerr = map[string]interface{}{\n\t\t\t\t\t\"code\":    dtmimp.JrpcCodeFailure,\n\t\t\t\t\t\"message\": err.Error(),\n\t\t\t\t}\n\t\t\t\t//// following is commented for server\n\t\t\t\t// } else if errors.Is(err, dtmcli.ErrOngoing) {\n\t\t\t\t// \tjerr = map[string]interface{}{\n\t\t\t\t// \t\t\"code\":    jrpcCodeOngoing,\n\t\t\t\t// \t\t\"message\": err.Error(),\n\t\t\t\t// \t}\n\t\t\t} else if jerr == nil {\n\t\t\t\tjerr = map[string]interface{}{\n\t\t\t\t\t\"code\":    -32603,\n\t\t\t\t\t\"message\": err.Error(),\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tresult := map[string]interface{}{\n\t\t\t\"jsonrpc\": \"2.0\",\n\t\t\t\"id\":      req.ID,\n\t\t\t\"error\":   jerr,\n\t\t\t\"result\":  r,\n\t\t}\n\t\tb, _ := json.Marshal(result)\n\t\tcont := string(b)\n\t\tif jerr == nil || jerr[\"code\"] == dtmimp.JrpcCodeOngoing {\n\t\t\tlogger.Infof(\"%2dms %d %s %s %s\", time.Since(began).Milliseconds(), 200, c.Request.Method, c.Request.RequestURI, cont)\n\t\t} else {\n\t\t\tlogger.Errorf(\"%2dms %d %s %s %s\", time.Since(began).Milliseconds(), 200, c.Request.Method, c.Request.RequestURI, cont)\n\t\t}\n\t\tc.JSON(200, result)\n\t})\n}\n\n// TransFromJrpcParams construct TransGlobal from jrpc params\nfunc TransFromJrpcParams(params interface{}) *TransGlobal {\n\tt := TransGlobal{}\n\tdtmimp.MustRemarshal(params, &t)\n\tt.setupPayloads()\n\treturn &t\n}\n\nfunc jrpcNewGid(interface{}) interface{} {\n\treturn map[string]interface{}{\"gid\": GenGid()}\n}\n\nfunc jrpcPrepare(params interface{}) interface{} {\n\treturn svcPrepare(TransFromJrpcParams(params))\n}\n\nfunc jrpcSubmit(params interface{}) interface{} {\n\treturn svcSubmit(TransFromJrpcParams(params))\n}\n\nfunc jrpcAbort(params interface{}) interface{} {\n\treturn svcAbort(TransFromJrpcParams(params))\n}\n\nfunc jrpcRegisterBranch(params interface{}) interface{} {\n\tdata := map[string]string{}\n\tdtmimp.MustRemarshal(params, &data)\n\tbranch := TransBranch{\n\t\tGid:      data[\"gid\"],\n\t\tBranchID: data[\"branch_id\"],\n\t\tStatus:   dtmcli.StatusPrepared,\n\t\tBinData:  []byte(data[\"data\"]),\n\t}\n\treturn svcRegisterBranch(data[\"trans_type\"], &branch, data)\n}\n"
  },
  {
    "path": "dtmsvr/config/config.go",
    "content": "package config\n\nimport (\n\t\"encoding/json\"\n\t\"io/ioutil\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\t\"github.com/dtm-labs/logger\"\n\t\"gopkg.in/yaml.v3\"\n)\n\nconst (\n\t// DtmMetricsPort is metric port\n\tDtmMetricsPort = 8889\n\t// Mysql is mysql driver\n\tMysql = \"mysql\"\n\t// Redis is redis driver\n\tRedis = \"redis\"\n\t// BoltDb is boltdb driver\n\tBoltDb = \"boltdb\"\n\t// Postgres is postgres driver\n\tPostgres = \"postgres\"\n\t// SQLServer is SQL Server driver\n\tSQLServer = \"sqlserver\"\n)\n\n// MicroService config type for microservice based grpc\ntype MicroService struct {\n\tDriver   string `yaml:\"Driver\" default:\"default\"`\n\tTarget   string `yaml:\"Target\"`\n\tEndPoint string `yaml:\"EndPoint\"`\n}\n\n// HTTPMicroService is the config type for microservice based on http, like springcloud\ntype HTTPMicroService struct {\n\tDriver          string `yaml:\"Driver\" default:\"default\"`\n\tRegistryType    string `yaml:\"RegistryType\" default:\"\"`\n\tRegistryAddress string `yaml:\"RegistryAddress\" default:\"\"`\n\tRegistryOptions string `yaml:\"RegistryOptions\" default:\"{}\"`\n\tTarget          string `yaml:\"Target\"`\n\tEndPoint        string `yaml:\"EndPoint\"`\n}\n\n// Log config customize log\ntype Log struct {\n\tOutputs            string `yaml:\"Outputs\" default:\"stderr\"`\n\tRotationEnable     int64  `yaml:\"RotationEnable\" default:\"0\"`\n\tRotationConfigJSON string `yaml:\"RotationConfigJSON\" default:\"{}\"`\n}\n\n// Store defines storage relevant info\ntype Store struct {\n\tDriver             string `yaml:\"Driver\" default:\"boltdb\"`\n\tHost               string `yaml:\"Host\"`\n\tPort               int64  `yaml:\"Port\"`\n\tUser               string `yaml:\"User\"`\n\tPassword           string `yaml:\"Password\"`\n\tDb                 string `yaml:\"Db\" default:\"dtm\"`\n\tSchema             string `yaml:\"Schema\" default:\"public\"`\n\tMaxOpenConns       int64  `yaml:\"MaxOpenConns\" default:\"500\"`\n\tMaxIdleConns       int64  `yaml:\"MaxIdleConns\" default:\"500\"`\n\tConnMaxLifeTime    int64  `yaml:\"ConnMaxLifeTime\" default:\"5\"`\n\tDataExpire         int64  `yaml:\"DataExpire\" default:\"604800\"`        // Trans data will expire in 7 days. only for redis/boltdb.\n\tFinishedDataExpire int64  `yaml:\"FinishedDataExpire\" default:\"86400\"` // finished Trans data will expire in 1 days. only for redis.\n\tRedisPrefix        string `yaml:\"RedisPrefix\" default:\"{a}\"`          // Redis storage prefix. store data to only one slot in cluster\n}\n\n// IsDB checks config driver is mysql or postgres\nfunc (s *Store) IsDB() bool {\n\treturn s.Driver == dtmcli.DBTypeMysql || s.Driver == dtmcli.DBTypePostgres || s.Driver == dtmcli.DBTypeSQLServer\n}\n\n// GetDBConf returns db conf info\nfunc (s *Store) GetDBConf() dtmcli.DBConf {\n\treturn dtmcli.DBConf{\n\t\tDriver:   s.Driver,\n\t\tHost:     s.Host,\n\t\tPort:     s.Port,\n\t\tUser:     s.User,\n\t\tPassword: s.Password,\n\t\tDb:       s.Db,\n\t\tSchema:   s.Schema,\n\t}\n}\n\n// Type is the type for the config of dtm server\ntype Type struct {\n\tStore                         Store            `yaml:\"Store\"`\n\tTransCronInterval             int64            `yaml:\"TransCronInterval\" default:\"3\"`\n\tTimeoutToFail                 int64            `yaml:\"TimeoutToFail\" default:\"35\"`\n\tRetryInterval                 int64            `yaml:\"RetryInterval\" default:\"10\"`\n\tRequestTimeout                int64            `yaml:\"RequestTimeout\" default:\"3\"`\n\tHTTPPort                      int64            `yaml:\"HttpPort\" default:\"36789\"`\n\tGrpcPort                      int64            `yaml:\"GrpcPort\" default:\"36790\"`\n\tJSONRPCPort                   int64            `yaml:\"JsonRpcPort\" default:\"36791\"`\n\tMicroService                  MicroService     `yaml:\"MicroService\"`\n\tHTTPMicroService              HTTPMicroService `yaml:\"HttpMicroService\"`\n\tUpdateBranchSync              int64            `yaml:\"UpdateBranchSync\" default:\"1\"`\n\tUpdateBranchAsyncGoroutineNum int64            `yaml:\"UpdateBranchAsyncGoroutineNum\" default:\"1\"`\n\tLogLevel                      string           `yaml:\"LogLevel\" default:\"info\"`\n\tLog                           Log              `yaml:\"Log\"`\n\tTimeZoneOffset                string           `yaml:\"TimeZoneOffset\"`\n\tConfigUpdateInterval          int64            `yaml:\"ConfigUpdateInterval\" default:\"3\"`\n\tAlertRetryLimit               int64            `yaml:\"AlertRetryLimit\" default:\"3\"`\n\tAlertWebHook                  string           `yaml:\"AlertWebHook\"`\n\tAdminBasePath                 string           `yaml:\"AdminBasePath\"`\n\tGrpcServiceConfig             string           `yaml:\"GrpcServiceConfig\" default:\"{\\\"loadBalancingConfig\\\": [{\\\"round_robin\\\":{}}]}\"`\n}\n\n// Config config\nvar Config = Type{}\n\n// MustLoadConfig load config from env and file\nfunc MustLoadConfig(confFile string) {\n\tloadFromEnv(\"\", &Config)\n\tif confFile != \"\" {\n\t\tcont, err := ioutil.ReadFile(confFile)\n\t\tlogger.FatalIfError(err)\n\t\terr = yaml.Unmarshal(cont, &Config)\n\t\tlogger.FatalIfError(err)\n\t}\n\tscont, err := json.MarshalIndent(&Config, \"\", \"  \")\n\tlogger.FatalIfError(err)\n\tlogger.Infof(\"config file: %s loaded config is: \\n%s\", confFile, scont)\n\terr = checkConfig(&Config)\n\tlogger.FatalfIf(err != nil, `config error: '%v'.\n\tplease visit http://d.dtm.pub to see the config document.`, err)\n\t// Set gRPC service config for client library\n\tif Config.GrpcServiceConfig == \"\" {\n\t\tConfig.GrpcServiceConfig = `{\"loadBalancingConfig\": [{\"round_robin\":{}}]}`\n\t}\n\t// Set config getter function for client library to read config directly\n\tdtmgimp.GrpcServiceConfigGetter = func() string {\n\t\treturn Config.GrpcServiceConfig\n\t}\n}\n"
  },
  {
    "path": "dtmsvr/config/config_test.go",
    "content": "package config\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestLoadFromEnv(t *testing.T) {\n\tassert.Equal(t, \"MICRO_SERVICE_DRIVER\", toUnderscoreUpper(\"MicroService_Driver\"))\n\n\tms := MicroService{}\n\tos.Setenv(\"T_DRIVER\", \"d1\")\n\tloadFromEnv(\"T\", &ms)\n\tassert.Equal(t, \"d1\", ms.Driver)\n}\n\nfunc TestLoadConfig(t *testing.T) {\n\tMustLoadConfig(\"../../conf.sample.yml\")\n\n\t//assert default value\n\tassert.Equal(t, int64(1), Config.UpdateBranchSync)\n}\nfunc TestCheckConfig(t *testing.T) {\n\tconf := Config\n\tconf.RetryInterval = 1\n\tretryIntervalErr := checkConfig(&conf)\n\tretryIntervalExpect := errors.New(\"RetryInterval should not be less than 10\")\n\tassert.Equal(t, retryIntervalErr, retryIntervalExpect)\n\n\tconf.RetryInterval = 10\n\tconf.TimeoutToFail = 5\n\ttimeoutToFailErr := checkConfig(&conf)\n\ttimeoutToFailExpect := errors.New(\"TimeoutToFail should not be less than RetryInterval\")\n\tassert.Equal(t, timeoutToFailErr, timeoutToFailExpect)\n\n\tconf.TimeoutToFail = 20\n\tdriverErr := checkConfig(&conf)\n\tassert.Equal(t, driverErr, nil)\n\n\tconf.Store = Store{Driver: Mysql}\n\thostErr := checkConfig(&conf)\n\thostExpect := errors.New(\"Db host not valid \")\n\tassert.Equal(t, hostErr, hostExpect)\n\n\tconf.Store = Store{Driver: Mysql, Host: \"127.0.0.1\"}\n\tportErr := checkConfig(&conf)\n\tportExpect := errors.New(\"Db port not valid \")\n\tassert.Equal(t, portErr, portExpect)\n\n\tconf.Store = Store{Driver: Mysql, Host: \"127.0.0.1\", Port: 8686}\n\tuserErr := checkConfig(&conf)\n\tuserExpect := errors.New(\"Db user not valid \")\n\tassert.Equal(t, userErr, userExpect)\n\n\tconf.Store = Store{Driver: Postgres, Host: \"127.0.0.1\", Port: 8686, User: \"postgres\", Schema: \"\"}\n\tschemaErr := checkConfig(&conf)\n\tschemaExpect := errors.New(\"Postgres schema not valid\")\n\tassert.Equal(t, schemaErr, schemaExpect)\n\n\tconf.Store = Store{Driver: Redis, Host: \"\", Port: 8686}\n\tassert.Equal(t, errors.New(\"Redis host not valid\"), checkConfig(&conf))\n\n\tconf.Store = Store{Driver: Redis, Host: \"127.0.0.1\", Port: 0}\n\tassert.Equal(t, errors.New(\"Redis port not valid\"), checkConfig(&conf))\n\n}\n\nfunc TestConfig(t *testing.T) {\n\ttestConfigStringField(&Config.Store.Driver, \"\", t)\n\ttestConfigStringField(&Config.Store.User, \"\", t)\n\ttestConfigIntField(&Config.RetryInterval, 9, t)\n\ttestConfigIntField(&Config.TimeoutToFail, 9, t)\n}\n\nfunc testConfigStringField(fd *string, val string, t *testing.T) {\n\told := *fd\n\t*fd = val\n\tstr := checkConfig(&Config)\n\tassert.NotEqual(t, \"\", str)\n\t*fd = old\n}\n\nfunc testConfigIntField(fd *int64, val int64, t *testing.T) {\n\told := *fd\n\t*fd = val\n\tstr := checkConfig(&Config)\n\tassert.NotEqual(t, \"\", str)\n\t*fd = old\n}\n"
  },
  {
    "path": "dtmsvr/config/config_utils.go",
    "content": "package config\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n)\n\nfunc loadFromEnv(prefix string, conf interface{}) {\n\trv := reflect.ValueOf(conf)\n\tdtmimp.PanicIf(rv.Kind() != reflect.Ptr || rv.IsNil(),\n\t\tfmt.Errorf(\"should be a valid pointer, but %s found\", reflect.TypeOf(conf).Name()))\n\tloadFromEnvInner(prefix, rv.Elem(), \"\")\n}\n\nfunc loadFromEnvInner(prefix string, conf reflect.Value, defaultValue string) {\n\tkind := conf.Kind()\n\tswitch kind {\n\tcase reflect.Struct:\n\t\tt := conf.Type()\n\t\tfor i := 0; i < t.NumField(); i++ {\n\t\t\ttag := t.Field(i).Tag\n\t\t\tloadFromEnvInner(prefix+\"_\"+tag.Get(\"yaml\"), conf.Field(i), tag.Get(\"default\"))\n\t\t}\n\tcase reflect.String:\n\t\tstr := os.Getenv(toUnderscoreUpper(prefix))\n\t\tif str == \"\" {\n\t\t\tstr = defaultValue\n\t\t}\n\t\tconf.Set(reflect.ValueOf(str))\n\tcase reflect.Int64:\n\t\tstr := os.Getenv(toUnderscoreUpper(prefix))\n\t\tif str == \"\" {\n\t\t\tstr = defaultValue\n\t\t}\n\t\tif str == \"\" {\n\t\t\tstr = \"0\"\n\t\t}\n\t\tconf.Set(reflect.ValueOf(int64(dtmimp.MustAtoi(str))))\n\tdefault:\n\t\tpanic(fmt.Errorf(\"unsupported type: %s\", conf.Type().Name()))\n\t}\n}\n\nfunc toUnderscoreUpper(key string) string {\n\tkey = strings.Trim(key, \"_\")\n\tmatchLastCap := regexp.MustCompile(\"([A-Z])([A-Z][a-z])\")\n\ts2 := matchLastCap.ReplaceAllString(key, \"${1}_${2}\")\n\n\tmatchFirstCap := regexp.MustCompile(\"([a-z])([A-Z]+)\")\n\ts2 = matchFirstCap.ReplaceAllString(s2, \"${1}_${2}\")\n\t// logger.Infof(\"loading from env: %s\", strings.ToUpper(s2))\n\treturn strings.ToUpper(s2)\n}\n\nfunc checkConfig(conf *Type) error {\n\tif conf.RetryInterval < 10 {\n\t\treturn errors.New(\"RetryInterval should not be less than 10\")\n\t}\n\tif conf.TimeoutToFail < conf.RetryInterval {\n\t\treturn errors.New(\"TimeoutToFail should not be less than RetryInterval\")\n\t}\n\tswitch conf.Store.Driver {\n\tcase BoltDb:\n\t\treturn nil\n\tcase Mysql, Postgres:\n\t\tif conf.Store.Host == \"\" {\n\t\t\treturn errors.New(\"Db host not valid \")\n\t\t}\n\t\tif conf.Store.Port == 0 {\n\t\t\treturn errors.New(\"Db port not valid \")\n\t\t}\n\t\tif conf.Store.User == \"\" {\n\t\t\treturn errors.New(\"Db user not valid \")\n\t\t}\n\t\tif conf.Store.Schema == \"\" {\n\t\t\treturn errors.New(\"Postgres schema not valid\")\n\t\t}\n\tcase Redis:\n\t\tif conf.Store.Host == \"\" {\n\t\t\treturn errors.New(\"Redis host not valid\")\n\t\t}\n\t\tif conf.Store.Port == 0 {\n\t\t\treturn errors.New(\"Redis port not valid\")\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "dtmsvr/cron.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmsvr\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"runtime/debug\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/logger\"\n)\n\n// NowForwardDuration will be set in test, trans may be timeout\nvar NowForwardDuration = time.Duration(0)\n\n// CronForwardDuration will be set in test. cron will fetch trans which expire in CronForwardDuration\nvar CronForwardDuration = time.Duration(0)\n\n// CronTransOnce cron expired trans. use expireIn as expire time\nfunc CronTransOnce() (gid string) {\n\tdefer handlePanic(nil)\n\ttrans := lockOneTrans(CronForwardDuration)\n\tif trans == nil {\n\t\treturn\n\t}\n\tgid = trans.Gid\n\ttrans.WaitResult = true\n\tbranches := GetStore().FindBranches(gid)\n\terr := trans.Process(branches)\n\tdtmimp.PanicIf(err != nil && !errors.Is(err, dtmcli.ErrFailure) && !errors.Is(err, dtmcli.ErrOngoing), err)\n\treturn\n}\n\n// CronExpiredTrans cron expired trans, num == -1 indicate for ever\nfunc CronExpiredTrans(num int) {\n\tfor i := 0; i < num || num == -1; i++ {\n\t\tgid := CronTransOnce()\n\t\tif gid == \"\" && num != 1 {\n\t\t\tsleepCronTime()\n\t\t}\n\t}\n}\n\n// CronUpdateTopicsMap cron updates topics map\nfunc CronUpdateTopicsMap() {\n\tfor {\n\t\ttime.Sleep(time.Duration(conf.ConfigUpdateInterval) * time.Second)\n\t\tCronUpdateTopicsMapOnce()\n\t}\n}\n\n// CronUpdateTopicsMapOnce cron updates topics map once\nfunc CronUpdateTopicsMapOnce() {\n\tdefer handlePanic(nil)\n\tupdateTopicsMap()\n}\n\nfunc lockOneTrans(expireIn time.Duration) *TransGlobal {\n\tglobal := GetStore().LockOneGlobalTrans(expireIn)\n\tif global == nil {\n\t\treturn nil\n\t}\n\tlogger.Infof(\"cron job return a trans: %s\", global.String())\n\treturn &TransGlobal{TransGlobalStore: *global}\n}\n\nfunc handlePanic(perr *error) {\n\tif err := recover(); err != nil {\n\t\tlogger.Errorf(\"----recovered panic %v\\n%s\", err, string(debug.Stack()))\n\t\tif perr != nil {\n\t\t\t*perr = fmt.Errorf(\"dtm panic: %v\", err)\n\t\t}\n\t}\n}\n\nfunc sleepCronTime() {\n\tnormal := time.Duration((float64(conf.TransCronInterval) - rand.Float64()) * float64(time.Second))\n\tinterval := dtmimp.If(CronForwardDuration > 0, 1*time.Millisecond, normal).(time.Duration)\n\tlogger.Debugf(\"sleeping for %v\", interval)\n\ttime.Sleep(interval)\n}\n"
  },
  {
    "path": "dtmsvr/entry/main.go",
    "content": "package entry\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmsvr\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/config\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage/registry\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/gin-gonic/gin\"\n\t\"go.uber.org/automaxprocs/maxprocs\"\n)\n\nfunc usage() {\n\tcmd := filepath.Base(os.Args[0])\n\ts := \"Usage: %s [options]\\n\\n\"\n\tfmt.Fprintf(os.Stderr, s, cmd)\n\tflag.PrintDefaults()\n}\n\nvar isVersion = flag.Bool(\"v\", false, \"Show the version of dtm.\")\nvar isDebug = flag.Bool(\"d\", false, \"Set log level to debug.\")\nvar isHelp = flag.Bool(\"h\", false, \"Show the help information about dtm.\")\nvar isReset = flag.Bool(\"r\", false, \"Reset dtm server data.\")\nvar confFile = flag.String(\"c\", \"\", \"Path to the server configuration file.\")\n\n// Main is the entry point of dtm server.\nfunc Main(version *string) (*gin.Engine, *config.Type) {\n\tflag.Parse()\n\tif *version == \"\" {\n\t\t*version = \"v0.0.0-dev\"\n\t}\n\tdtmsvr.Version = *version\n\tif flag.NArg() > 0 || *isHelp {\n\t\tusage()\n\t\treturn nil, nil\n\t} else if *isVersion {\n\t\tfmt.Printf(\"dtm version: %s\\n\", *version)\n\t\treturn nil, nil\n\t}\n\tlogger.Infof(\"dtm version is: %s\", *version)\n\tconfig.MustLoadConfig(*confFile)\n\tif config.Config.TimeZoneOffset != \"\" {\n\t\ttime.Local = time.FixedZone(\"UTC\", dtmimp.MustAtoi(config.Config.TimeZoneOffset)*3600)\n\t}\n\tconf := &config.Config\n\tif *isDebug {\n\t\tconf.LogLevel = \"debug\"\n\t}\n\tlogger.InitLog2(conf.LogLevel, conf.Log.Outputs, conf.Log.RotationEnable, conf.Log.RotationConfigJSON)\n\tif *isReset {\n\t\tdtmsvr.PopulateDB(false)\n\t}\n\t_, _ = maxprocs.Set(maxprocs.Logger(logger.Infof))\n\tregistry.WaitStoreUp()\n\tapp := dtmsvr.StartSvr()       // start dtmsvr api\n\tgo dtmsvr.CronExpiredTrans(-1) // start dtmsvr cron job\n\treturn app, &config.Config\n}\n"
  },
  {
    "path": "dtmsvr/metrics.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmsvr\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/prometheus/client_golang/prometheus\"\n\t\"github.com/prometheus/client_golang/prometheus/promauto\"\n\t\"google.golang.org/grpc\"\n)\n\nvar (\n\tserverInfoGauge = promauto.NewGaugeVec(prometheus.GaugeOpts{\n\t\tName: \"dtm_server_info\",\n\t\tHelp: \"The information of this dtm server.\",\n\t},\n\t\t[]string{\"gin_version\", \"grpc_version\"})\n\n\tprocessTotal = promauto.NewCounterVec(prometheus.CounterOpts{\n\t\tName: \"dtm_server_process_total\",\n\t\tHelp: \"All request received by dtm\",\n\t},\n\t\t[]string{\"type\", \"api\", \"status\"})\n\n\tresponseTime = promauto.NewHistogramVec(prometheus.HistogramOpts{\n\t\tName: \"dtm_server_response_duration\",\n\t\tHelp: \"The request durations of a dtm server api\",\n\t},\n\t\t[]string{\"type\", \"api\"})\n\n\ttransactionTotal = promauto.NewCounterVec(prometheus.CounterOpts{\n\t\tName: \"dtm_transaction_process_total\",\n\t\tHelp: \"All transactions processed by dtm\",\n\t},\n\t\t[]string{\"model\", \"status\"})\n\n\ttransactionHandledTime = promauto.NewHistogramVec(prometheus.HistogramOpts{\n\t\tName: \"dtm_transaction_handled_duration\",\n\t\tHelp: \"Histogram of handling latency of the transaction that handled by the server.\",\n\t},\n\t\t[]string{\"model\"})\n\n\tbranchTotal = promauto.NewCounterVec(prometheus.CounterOpts{\n\t\tName: \"dtm_branch_process_total\",\n\t\tHelp: \"All branches processed by dtm\",\n\t},\n\t\t[]string{\"model\", \"branchid\", \"branchtype\", \"status\"})\n)\n\nfunc setServerInfoMetrics() {\n\tserverInfoGauge.WithLabelValues(gin.Version, grpc.Version).Set(1)\n}\n\nfunc httpMetrics(app *gin.Engine) *gin.Engine {\n\tapp.Use(func(c *gin.Context) {\n\t\tapi := extractFromPath(c.Request.RequestURI)\n\t\ttimer := prometheus.NewTimer(prometheus.ObserverFunc(func(v float64) {\n\t\t\tresponseTime.WithLabelValues(\"http\", api).Observe(v)\n\t\t}))\n\t\tdefer timer.ObserveDuration()\n\t\tc.Next()\n\t\tstatus := c.Writer.Status()\n\t\tif status >= 400 {\n\t\t\tprocessTotal.WithLabelValues(\"http\", api, \"fail\").Inc()\n\t\t} else {\n\t\t\tprocessTotal.WithLabelValues(\"http\", api, \"ok\").Inc()\n\t\t}\n\t})\n\treturn app\n}\n\nfunc grpcMetrics(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {\n\tapi := extractFromPath(info.FullMethod)\n\ttimer := prometheus.NewTimer(prometheus.ObserverFunc(func(v float64) {\n\t\tresponseTime.WithLabelValues(\"grpc\", api).Observe(v)\n\t}))\n\tdefer timer.ObserveDuration()\n\tm, err := handler(ctx, req)\n\tif err != nil {\n\t\tprocessTotal.WithLabelValues(\"grpc\", api, \"fail\").Inc()\n\t} else {\n\t\tprocessTotal.WithLabelValues(\"grpc\", api, \"ok\").Inc()\n\t}\n\treturn m, err\n}\n\nfunc transactionMetrics(global *TransGlobal, status bool) {\n\tif status {\n\t\ttransactionTotal.WithLabelValues(global.TransType, \"ok\").Inc()\n\t} else {\n\t\ttransactionTotal.WithLabelValues(global.TransType, \"fail\").Inc()\n\t}\n\ttransactionHandledTime.WithLabelValues(global.TransType).Observe(time.Since(*global.CreateTime).Seconds())\n}\n\nfunc branchMetrics(global *TransGlobal, branch *TransBranch, status bool) {\n\tif status {\n\t\tbranchTotal.WithLabelValues(global.TransType, branch.BranchID, branch.Op, \"ok\").Inc()\n\t} else {\n\t\tbranchTotal.WithLabelValues(global.TransType, branch.BranchID, branch.Op, \"fail\").Inc()\n\t}\n}\n\nfunc extractFromPath(val string) string {\n\tstrs := strings.Split(val, \"/\")\n\treturn strings.ToLower(strs[len(strs)-1])\n}\n"
  },
  {
    "path": "dtmsvr/microservices/drivers.go",
    "content": "package microservices\n\nimport (\n\t// load the microserver drivers\n\t_ \"github.com/dtm-labs/dtmdriver-dapr\"\n\t_ \"github.com/dtm-labs/dtmdriver-ego\"\n\t_ \"github.com/dtm-labs/dtmdriver-gozero\"\n\t_ \"github.com/dtm-labs/dtmdriver-kratos\"\n\t_ \"github.com/dtm-labs/dtmdriver-polaris\"\n\t_ \"github.com/dtm-labs/dtmdriver-springcloud\"\n\t_ \"github.com/zhufuyi/dtmdriver-sponge\"\n)\n"
  },
  {
    "path": "dtmsvr/storage/boltdb/boltdb.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n// package boltdb implement the storage for boltdb\npackage boltdb\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/logger\"\n\tbolt \"go.etcd.io/bbolt\"\n)\n\n// Store implements storage.Store, and storage with boltdb\ntype Store struct {\n\tboltDb *bolt.DB\n\n\tdataExpire    int64\n\tretryInterval int64\n}\n\n// NewStore will return the boltdb implement\n// TODO: change to options\nfunc NewStore(dataExpire int64, retryInterval int64) *Store {\n\ts := &Store{\n\t\tdataExpire:    dataExpire,\n\t\tretryInterval: retryInterval,\n\t}\n\n\tdb, err := bolt.Open(\"./dtm.bolt\", 0666, &bolt.Options{Timeout: 1 * time.Second})\n\tdtmimp.E2P(err)\n\n\t// NOTE: we must ensure all buckets is exists before we use it\n\terr = initializeBuckets(db)\n\tdtmimp.E2P(err)\n\n\t// TODO:\n\t//   1. refactor this code\n\t//   2. make cleanup run period, to avoid the file growup when server long-running\n\terr = cleanupExpiredData(\n\t\ttime.Duration(dataExpire)*time.Second,\n\t\tdb,\n\t)\n\tdtmimp.E2P(err)\n\n\ts.boltDb = db\n\treturn s\n}\n\nfunc initializeBuckets(db *bolt.DB) error {\n\treturn db.Update(func(t *bolt.Tx) error {\n\t\tfor _, bucket := range allBuckets {\n\t\t\t_, err := t.CreateBucketIfNotExists(bucket)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t})\n}\n\n// cleanupExpiredData will clean the expired data in boltdb, the\n//\n//\texpired time is configurable.\nfunc cleanupExpiredData(expire time.Duration, db *bolt.DB) error {\n\tif expire <= 0 {\n\t\treturn nil\n\t}\n\n\tlastKeepTime := time.Now().Add(-expire)\n\treturn db.Update(func(t *bolt.Tx) error {\n\t\tglobalBucket := t.Bucket(bucketGlobal)\n\t\tif globalBucket == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\texpiredGids := map[string]struct{}{}\n\t\tcursor := globalBucket.Cursor()\n\t\tfor k, v := cursor.First(); k != nil; k, v = cursor.Next() {\n\t\t\ttrans := storage.TransGlobalStore{}\n\t\t\tdtmimp.MustUnmarshal(v, &trans)\n\n\t\t\ttransDoneTime := trans.FinishTime\n\t\t\tif transDoneTime == nil {\n\t\t\t\ttransDoneTime = trans.RollbackTime\n\t\t\t}\n\t\t\tif transDoneTime != nil && lastKeepTime.After(*transDoneTime) {\n\t\t\t\texpiredGids[string(k)] = struct{}{}\n\t\t\t}\n\t\t}\n\n\t\tcleanupGlobalWithGids(t, expiredGids)\n\t\tcleanupBranchWithGids(t, expiredGids)\n\t\tcleanupIndexWithGids(t, expiredGids)\n\t\treturn nil\n\t})\n}\n\nfunc cleanupGlobalWithGids(t *bolt.Tx, gids map[string]struct{}) {\n\tbucket := t.Bucket(bucketGlobal)\n\tif bucket == nil {\n\t\treturn\n\t}\n\n\tlogger.Debugf(\"Start to cleanup %d gids\", len(gids))\n\tfor gid := range gids {\n\t\tlogger.Debugf(\"Start to delete gid: %s\", gid)\n\t\tdtmimp.E2P(bucket.Delete([]byte(gid)))\n\t}\n}\n\nfunc cleanupBranchWithGids(t *bolt.Tx, gids map[string]struct{}) {\n\tbucket := t.Bucket(bucketBranches)\n\tif bucket == nil {\n\t\treturn\n\t}\n\n\t// It's not safe if we delete the item when use cursor, for more detail see\n\t//    https://github.com/etcd-io/bbolt/issues/146\n\tbranchKeys := []string{}\n\tfor gid := range gids {\n\t\tcursor := bucket.Cursor()\n\t\tfor k, v := cursor.Seek([]byte(gid)); k != nil; k, v = cursor.Next() {\n\t\t\tb := storage.TransBranchStore{}\n\t\t\tdtmimp.MustUnmarshal(v, &b)\n\t\t\tif b.Gid != gid {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tbranchKeys = append(branchKeys, string(k))\n\t\t}\n\t}\n\n\tlogger.Debugf(\"Start to cleanup %d branches\", len(branchKeys))\n\tfor _, key := range branchKeys {\n\t\tlogger.Debugf(\"Start to delete branch: %s\", key)\n\t\tdtmimp.E2P(bucket.Delete([]byte(key)))\n\t}\n}\n\nfunc cleanupIndexWithGids(t *bolt.Tx, gids map[string]struct{}) {\n\tbucket := t.Bucket(bucketIndex)\n\tif bucket == nil {\n\t\treturn\n\t}\n\n\tindexKeys := []string{}\n\tcursor := bucket.Cursor()\n\tfor k, _ := cursor.First(); k != nil; k, _ = cursor.Next() {\n\t\tks := strings.Split(string(k), \"-\")\n\t\tif len(ks) != 2 {\n\t\t\tcontinue\n\t\t}\n\n\t\tif _, ok := gids[ks[1]]; ok {\n\t\t\tindexKeys = append(indexKeys, string(k))\n\t\t}\n\t}\n\n\tlogger.Debugf(\"Start to cleanup %d indexes\", len(indexKeys))\n\tfor _, key := range indexKeys {\n\t\tlogger.Debugf(\"Start to delete index: %s\", key)\n\t\tdtmimp.E2P(bucket.Delete([]byte(key)))\n\t}\n}\n\nvar bucketGlobal = []byte(\"global\")\nvar bucketBranches = []byte(\"branches\")\nvar bucketIndex = []byte(\"index\")\nvar bucketKV = []byte(\"kv\")\nvar allBuckets = [][]byte{\n\tbucketBranches,\n\tbucketGlobal,\n\tbucketIndex,\n\tbucketKV,\n}\n\nfunc tGetGlobal(t *bolt.Tx, gid string) *storage.TransGlobalStore {\n\ttrans := storage.TransGlobalStore{}\n\tbs := t.Bucket(bucketGlobal).Get([]byte(gid))\n\tif bs == nil {\n\t\treturn nil\n\t}\n\tdtmimp.MustUnmarshal(bs, &trans)\n\treturn &trans\n}\n\nfunc tGetBranches(t *bolt.Tx, gid string) []storage.TransBranchStore {\n\tbranches := []storage.TransBranchStore{}\n\tcursor := t.Bucket(bucketBranches).Cursor()\n\tfor k, v := cursor.Seek([]byte(gid)); k != nil; k, v = cursor.Next() {\n\t\tb := storage.TransBranchStore{}\n\t\tdtmimp.MustUnmarshal(v, &b)\n\t\tif b.Gid != gid {\n\t\t\tbreak\n\t\t}\n\t\tbranches = append(branches, b)\n\t}\n\treturn branches\n}\nfunc tPutGlobal(t *bolt.Tx, global *storage.TransGlobalStore) {\n\tbs := dtmimp.MustMarshal(global)\n\terr := t.Bucket(bucketGlobal).Put([]byte(global.Gid), bs)\n\tdtmimp.E2P(err)\n}\n\nfunc tPutBranches(t *bolt.Tx, branches []storage.TransBranchStore, start int64) {\n\terr := tPutBranches2(t, branches, start)\n\tdtmimp.E2P(err)\n}\n\nfunc tPutBranches2(t *bolt.Tx, branches []storage.TransBranchStore, start int64) error {\n\tif start == -1 {\n\t\tb0 := &branches[0]\n\t\tbs := tGetBranches(t, b0.Gid)\n\t\tfor _, b := range bs {\n\t\t\tif b.BranchID == b0.BranchID && b.Op == b0.Op {\n\t\t\t\treturn storage.ErrUniqueConflict\n\t\t\t}\n\t\t}\n\t\tstart = int64(len(bs))\n\t}\n\tfor i, b := range branches {\n\t\tk := b.Gid + fmt.Sprintf(\"%03d\", i+int(start))\n\t\tv := dtmimp.MustMarshalString(b)\n\t\terr := t.Bucket(bucketBranches).Put([]byte(k), []byte(v))\n\t\tdtmimp.E2P(err)\n\t}\n\treturn nil\n}\n\nfunc tDelIndex(t *bolt.Tx, unix int64, gid string) {\n\tk := fmt.Sprintf(\"%d-%s\", unix, gid)\n\terr := t.Bucket(bucketIndex).Delete([]byte(k))\n\tdtmimp.E2P(err)\n}\n\nfunc tPutIndex(t *bolt.Tx, unix int64, gid string) {\n\tk := fmt.Sprintf(\"%d-%s\", unix, gid)\n\terr := t.Bucket(bucketIndex).Put([]byte(k), []byte(gid))\n\tdtmimp.E2P(err)\n}\n\nfunc tGetKV(t *bolt.Tx, cat, key string) *storage.KVStore {\n\tk := fmt.Sprintf(\"%s-%s\", cat, key)\n\tkv := storage.KVStore{}\n\tres := t.Bucket(bucketKV).Get([]byte(k))\n\tif res == nil {\n\t\treturn nil\n\t}\n\tdtmimp.MustUnmarshal(res, &kv)\n\treturn &kv\n}\n\nfunc tPutKV(t *bolt.Tx, kv *storage.KVStore) {\n\tk := fmt.Sprintf(\"%s-%s\", kv.Cat, kv.K)\n\tkvJSON := dtmimp.MustMarshal(kv)\n\terr := t.Bucket(bucketKV).Put([]byte(k), kvJSON)\n\tdtmimp.E2P(err)\n}\n\nfunc tDelKV(t *bolt.Tx, cat, key string) {\n\tk := fmt.Sprintf(\"%s-%s\", cat, key)\n\terr := t.Bucket(bucketKV).Delete([]byte(k))\n\tdtmimp.E2P(err)\n}\n\n// Ping execs ping cmd to boltdb\nfunc (s *Store) Ping() error {\n\treturn nil\n}\n\n// PopulateData populates data to boltdb\nfunc (s *Store) PopulateData(skipDrop bool) {\n\tif !skipDrop {\n\t\terr := s.boltDb.Update(func(t *bolt.Tx) error {\n\t\t\tdtmimp.E2P(t.DeleteBucket(bucketIndex))\n\t\t\tdtmimp.E2P(t.DeleteBucket(bucketBranches))\n\t\t\tdtmimp.E2P(t.DeleteBucket(bucketGlobal))\n\t\t\tdtmimp.E2P(t.DeleteBucket(bucketKV))\n\t\t\t_, err := t.CreateBucket(bucketIndex)\n\t\t\tdtmimp.E2P(err)\n\t\t\t_, err = t.CreateBucket(bucketBranches)\n\t\t\tdtmimp.E2P(err)\n\t\t\t_, err = t.CreateBucket(bucketGlobal)\n\t\t\tdtmimp.E2P(err)\n\t\t\t_, err = t.CreateBucket(bucketKV)\n\t\t\tdtmimp.E2P(err)\n\t\t\treturn nil\n\t\t})\n\t\tdtmimp.E2P(err)\n\t\tlogger.Infof(\"Reset all data for boltdb\")\n\t}\n}\n\n// FindTransGlobalStore finds GlobalTrans data by gid\nfunc (s *Store) FindTransGlobalStore(gid string) (trans *storage.TransGlobalStore) {\n\terr := s.boltDb.View(func(t *bolt.Tx) error {\n\t\ttrans = tGetGlobal(t, gid)\n\t\treturn nil\n\t})\n\tdtmimp.E2P(err)\n\treturn\n}\n\n// ScanTransGlobalStores lists GlobalTrans data\nfunc (s *Store) ScanTransGlobalStores(position *string, limit int64, condition storage.TransGlobalScanCondition) []storage.TransGlobalStore {\n\tglobals := []storage.TransGlobalStore{}\n\terr := s.boltDb.View(func(t *bolt.Tx) error {\n\t\tcursor := t.Bucket(bucketGlobal).Cursor()\n\t\tfor k, v := cursor.Seek([]byte(*position)); k != nil; k, v = cursor.Next() {\n\t\t\tif string(k) == *position {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tg := storage.TransGlobalStore{}\n\t\t\tdtmimp.MustUnmarshal(v, &g)\n\t\t\tif !((condition.Status == \"\" || g.Status == condition.Status) &&\n\t\t\t\t(condition.TransType == \"\" || g.TransType == condition.TransType) &&\n\t\t\t\t(condition.CreateTimeStart.IsZero() || g.CreateTime.After(condition.CreateTimeStart)) &&\n\t\t\t\t(condition.CreateTimeEnd.IsZero() || g.CreateTime.Before(condition.CreateTimeEnd))) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tglobals = append(globals, g)\n\t\t\tif len(globals) == int(limit) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\tdtmimp.E2P(err)\n\tif len(globals) < int(limit) {\n\t\t*position = \"\"\n\t} else {\n\t\t*position = globals[len(globals)-1].Gid\n\t}\n\treturn globals\n}\n\n// FindBranches finds Branch data by gid\nfunc (s *Store) FindBranches(gid string) []storage.TransBranchStore {\n\tvar branches []storage.TransBranchStore\n\terr := s.boltDb.View(func(t *bolt.Tx) error {\n\t\tbranches = tGetBranches(t, gid)\n\t\treturn nil\n\t})\n\tdtmimp.E2P(err)\n\treturn branches\n}\n\n// UpdateBranches update branches info\nfunc (s *Store) UpdateBranches(branches []storage.TransBranchStore, updates []string) (int, error) {\n\treturn 0, nil // not implemented\n}\n\n// LockGlobalSaveBranches creates branches\nfunc (s *Store) LockGlobalSaveBranches(gid string, status string, branches []storage.TransBranchStore, branchStart int) {\n\terr := s.boltDb.Update(func(t *bolt.Tx) error {\n\t\tg := tGetGlobal(t, gid)\n\t\tif g == nil {\n\t\t\treturn storage.ErrNotFound\n\t\t}\n\t\tif g.Status != status {\n\t\t\treturn storage.ErrNotFound\n\t\t}\n\t\treturn tPutBranches2(t, branches, int64(branchStart))\n\t})\n\tdtmimp.E2P(err)\n}\n\n// MaySaveNewTrans creates a new trans\nfunc (s *Store) MaySaveNewTrans(global *storage.TransGlobalStore, branches []storage.TransBranchStore) error {\n\treturn s.boltDb.Update(func(t *bolt.Tx) error {\n\t\tg := tGetGlobal(t, global.Gid)\n\t\tif g != nil {\n\t\t\treturn storage.ErrUniqueConflict\n\t\t}\n\t\ttPutGlobal(t, global)\n\t\ttPutIndex(t, global.NextCronTime.Unix(), global.Gid)\n\t\ttPutBranches(t, branches, 0)\n\t\treturn nil\n\t})\n}\n\n// ChangeGlobalStatus changes global trans status\nfunc (s *Store) ChangeGlobalStatus(global *storage.TransGlobalStore, newStatus string, updates []string, finished bool) {\n\told := global.Status\n\tglobal.Status = newStatus\n\terr := s.boltDb.Update(func(t *bolt.Tx) error {\n\t\tg := tGetGlobal(t, global.Gid)\n\t\tif g == nil || g.Status != old {\n\t\t\treturn storage.ErrNotFound\n\t\t}\n\t\tif finished {\n\t\t\ttDelIndex(t, g.NextCronTime.Unix(), g.Gid)\n\t\t}\n\t\ttPutGlobal(t, global)\n\t\treturn nil\n\t})\n\tdtmimp.E2P(err)\n}\n\n// TouchCronTime updates cronTime\nfunc (s *Store) TouchCronTime(global *storage.TransGlobalStore, nextCronInterval int64, nextCronTime *time.Time) {\n\toldUnix := global.NextCronTime.Unix()\n\tglobal.UpdateTime = dtmutil.GetNextTime(0)\n\tglobal.NextCronTime = nextCronTime\n\tglobal.NextCronInterval = nextCronInterval\n\terr := s.boltDb.Update(func(t *bolt.Tx) error {\n\t\tg := tGetGlobal(t, global.Gid)\n\t\tif g == nil || g.Gid != global.Gid {\n\t\t\treturn storage.ErrNotFound\n\t\t}\n\t\ttDelIndex(t, oldUnix, global.Gid)\n\t\ttPutGlobal(t, global)\n\t\ttPutIndex(t, global.NextCronTime.Unix(), global.Gid)\n\t\treturn nil\n\t})\n\tdtmimp.E2P(err)\n}\n\n// LockOneGlobalTrans finds GlobalTrans\nfunc (s *Store) LockOneGlobalTrans(expireIn time.Duration) *storage.TransGlobalStore {\n\tvar trans *storage.TransGlobalStore\n\tmin1 := fmt.Sprintf(\"%d\", time.Now().Add(expireIn).Unix())\n\terr := s.boltDb.Update(func(t *bolt.Tx) error {\n\t\tcursor := t.Bucket(bucketIndex).Cursor()\n\t\ttoDelete := [][]byte{}\n\t\tfor k, v := cursor.First(); k != nil && string(k) <= min1 && (trans == nil || trans.IsFinished()); k, v = cursor.Next() {\n\t\t\ttrans = tGetGlobal(t, string(v))\n\t\t\ttoDelete = append(toDelete, k)\n\t\t}\n\t\tfor _, k := range toDelete {\n\t\t\terr := t.Bucket(bucketIndex).Delete(k)\n\t\t\tdtmimp.E2P(err)\n\t\t}\n\t\tif trans != nil && !trans.IsFinished() {\n\t\t\tnext := time.Now().Add(time.Duration(s.retryInterval) * time.Second)\n\t\t\ttrans.NextCronTime = &next\n\t\t\ttPutGlobal(t, trans)\n\t\t\t// this put should be after delete, because the data may be the same\n\t\t\ttPutIndex(t, next.Unix(), trans.Gid)\n\t\t}\n\t\treturn nil\n\t})\n\tdtmimp.E2P(err)\n\treturn trans\n}\n\n// ResetCronTime reset nextCronTime\n// unfinished transactions need to be retried as soon as possible after business downtime is recovered\nfunc (s *Store) ResetCronTime(after time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error) {\n\tnext := time.Now()\n\tvar trans *storage.TransGlobalStore\n\tmin1 := fmt.Sprintf(\"%d\", time.Now().Add(after).Unix())\n\terr = s.boltDb.Update(func(t *bolt.Tx) error {\n\t\tcursor := t.Bucket(bucketIndex).Cursor()\n\t\tsucceedCount = 0\n\t\tfor k, v := cursor.Seek([]byte(min1)); k != nil && succeedCount <= limit; k, v = cursor.Next() {\n\t\t\tif succeedCount == limit {\n\t\t\t\thasRemaining = true\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\ttrans = tGetGlobal(t, string(v))\n\t\t\terr := t.Bucket(bucketIndex).Delete(k)\n\t\t\tdtmimp.E2P(err)\n\n\t\t\ttrans.NextCronTime = &next\n\t\t\ttPutGlobal(t, trans)\n\t\t\ttPutIndex(t, next.Unix(), trans.Gid)\n\t\t\tsucceedCount++\n\t\t}\n\t\treturn nil\n\t})\n\treturn\n}\n\n// ResetTransGlobalCronTime reset nextCronTime of one global trans.\nfunc (s *Store) ResetTransGlobalCronTime(g *storage.TransGlobalStore) error {\n\terr := s.boltDb.Update(func(t *bolt.Tx) error {\n\t\tg := tGetGlobal(t, g.Gid)\n\t\tif g == nil {\n\t\t\treturn storage.ErrNotFound\n\t\t}\n\t\tnow := dtmutil.GetNextTime(0)\n\t\tg.NextCronTime = now\n\t\tg.UpdateTime = now\n\t\ttPutGlobal(t, g)\n\t\treturn nil\n\t})\n\tdtmimp.E2P(err)\n\treturn err\n}\n\n// ScanKV lists KV pairs\nfunc (s *Store) ScanKV(cat string, position *string, limit int64) []storage.KVStore {\n\tkvs := []storage.KVStore{}\n\terr := s.boltDb.View(func(t *bolt.Tx) error {\n\t\tcursor := t.Bucket(bucketKV).Cursor()\n\t\tfor k, v := cursor.Seek([]byte(*position)); k != nil; k, v = cursor.Next() {\n\t\t\tif string(k) == *position {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif !strings.HasPrefix(string(k), cat) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tkv := storage.KVStore{}\n\t\t\tdtmimp.MustUnmarshal(v, &kv)\n\t\t\tkvs = append(kvs, kv)\n\t\t\tif len(kvs) == int(limit) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\tdtmimp.E2P(err)\n\tif len(kvs) < int(limit) {\n\t\t*position = \"\"\n\t} else {\n\t\t*position = fmt.Sprintf(\"%s-%s\", cat, kvs[len(kvs)-1].K)\n\t}\n\treturn kvs\n}\n\n// FindKV finds key-value pairs\nfunc (s *Store) FindKV(cat, key string) []storage.KVStore {\n\tkvs := []storage.KVStore{}\n\tif cat != \"\" && key != \"\" {\n\t\terr := s.boltDb.View(func(t *bolt.Tx) error {\n\t\t\tkv := tGetKV(t, cat, key)\n\t\t\tif kv != nil {\n\t\t\t\tkvs = append(kvs, *kv)\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t\tdtmimp.E2P(err)\n\t\treturn kvs\n\t}\n\terr := s.boltDb.View(func(t *bolt.Tx) error {\n\t\tcursor := t.Bucket(bucketKV).Cursor()\n\t\tfor k, v := cursor.First(); k != nil; k, v = cursor.Next() {\n\t\t\tif !strings.HasPrefix(string(k), cat) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tkv := storage.KVStore{}\n\t\t\tdtmimp.MustUnmarshal(v, &kv)\n\t\t\tkvs = append(kvs, kv)\n\t\t}\n\t\treturn nil\n\t})\n\tdtmimp.E2P(err)\n\treturn kvs\n}\n\n// UpdateKV updates key-value pair\nfunc (s *Store) UpdateKV(kv *storage.KVStore) error {\n\tnow := time.Now()\n\tkv.UpdateTime = &now\n\toldVersion := kv.Version\n\tkv.Version = oldVersion + 1\n\n\treturn s.boltDb.Update(func(t *bolt.Tx) error {\n\t\tres := tGetKV(t, kv.Cat, kv.K)\n\t\tif res == nil || res.Version != oldVersion {\n\t\t\treturn storage.ErrNotFound\n\t\t}\n\t\ttPutKV(t, kv)\n\t\treturn nil\n\t})\n}\n\n// DeleteKV deletes key-value pair\nfunc (s *Store) DeleteKV(cat, key string) error {\n\treturn s.boltDb.Update(func(t *bolt.Tx) error {\n\t\tres := tGetKV(t, cat, key)\n\t\tif res == nil {\n\t\t\treturn storage.ErrNotFound\n\t\t}\n\t\ttDelKV(t, cat, key)\n\t\treturn nil\n\t})\n}\n\n// CreateKV creates key-value pair\nfunc (s *Store) CreateKV(cat, key, value string) error {\n\tnow := time.Now()\n\tkv := &storage.KVStore{\n\t\tModelBase: dtmutil.ModelBase{\n\t\t\tCreateTime: &now,\n\t\t\tUpdateTime: &now,\n\t\t},\n\t\tCat:     cat,\n\t\tK:       key,\n\t\tV:       value,\n\t\tVersion: 1,\n\t}\n\treturn s.boltDb.Update(func(t *bolt.Tx) error {\n\t\tres := tGetKV(t, cat, key)\n\t\tif res != nil {\n\t\t\treturn storage.ErrUniqueConflict\n\t\t}\n\t\ttPutKV(t, kv)\n\t\treturn nil\n\t})\n}\n"
  },
  {
    "path": "dtmsvr/storage/boltdb/boltdb_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage boltdb\n\nimport (\n\t\"path\"\n\t\"testing\"\n\t\"time\"\n\n\tga \"github.com/onsi/gomega\"\n\tbolt \"go.etcd.io/bbolt\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage\"\n)\n\nfunc TestInitializeBuckets(t *testing.T) {\n\tt.Run(\"normal test\", func(t *testing.T) {\n\t\tg := ga.NewWithT(t)\n\t\tdb, err := bolt.Open(path.Join(t.TempDir(), \"./test.bolt\"), 0666, &bolt.Options{Timeout: 1 * time.Second})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t\tdefer db.Close()\n\n\t\terr = initializeBuckets(db)\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\n\t\tactualBuckets := [][]byte{}\n\t\terr = db.View(func(t *bolt.Tx) error {\n\t\t\treturn t.ForEach(func(name []byte, _ *bolt.Bucket) error {\n\t\t\t\tactualBuckets = append(actualBuckets, name)\n\t\t\t\treturn nil\n\t\t\t})\n\t\t})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\n\t\tg.Expect(actualBuckets).To(ga.Equal(allBuckets))\n\t})\n}\n\nfunc TestCleanupExpiredData(t *testing.T) {\n\tt.Run(\"negative expired seconds\", func(t *testing.T) {\n\t\tg := ga.NewWithT(t)\n\t\tdb, err := bolt.Open(path.Join(t.TempDir(), \"./test.bolt\"), 0666, &bolt.Options{Timeout: 1 * time.Second})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t\tdefer db.Close()\n\n\t\terr = cleanupExpiredData(-1*time.Second, db)\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t})\n\n\tt.Run(\"nil global bucket\", func(t *testing.T) {\n\t\tg := ga.NewWithT(t)\n\t\tdb, err := bolt.Open(path.Join(t.TempDir(), \"./test.bolt\"), 0666, &bolt.Options{Timeout: 1 * time.Second})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t\tdefer db.Close()\n\n\t\terr = cleanupExpiredData(time.Second, db)\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t})\n\n\tt.Run(\"normal test\", func(t *testing.T) {\n\t\tg := ga.NewWithT(t)\n\t\tdb, err := bolt.Open(path.Join(t.TempDir(), \"./test.bolt\"), 0666, &bolt.Options{Timeout: 1 * time.Second})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t\tdefer db.Close()\n\n\t\t// Initialize data\n\t\terr = initializeBuckets(db)\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\n\t\terr = db.Update(func(t *bolt.Tx) error {\n\t\t\tdoneTime := time.Now().Add(-10 * time.Minute)\n\n\t\t\tgids := []string{\"gid0\", \"gid1\", \"gid2\"}\n\t\t\tgidDatas := []storage.TransGlobalStore{\n\t\t\t\t{}, // not finished\n\t\t\t\t{\n\t\t\t\t\tFinishTime: &doneTime,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tRollbackTime: &doneTime,\n\t\t\t\t},\n\t\t\t}\n\t\t\tbucket := t.Bucket(bucketGlobal)\n\t\t\tfor i := 0; i < len(gids); i++ {\n\t\t\t\terr = bucket.Put([]byte(gids[i]), dtmimp.MustMarshal(gidDatas[i]))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn nil\n\t\t})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\n\t\terr = cleanupExpiredData(time.Minute, db)\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\n\t\tactualGids := []string{}\n\t\terr = db.View(func(t *bolt.Tx) error {\n\t\t\tcursor := t.Bucket(bucketGlobal).Cursor()\n\t\t\tfor k, _ := cursor.First(); k != nil; k, _ = cursor.Next() {\n\t\t\t\tactualGids = append(actualGids, string(k))\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t\tg.Expect(actualGids).To(ga.Equal([]string{\"gid0\"}))\n\t})\n}\n\nfunc TestCleanupGlobalWithGids(t *testing.T) {\n\tt.Run(\"nil bucket\", func(t *testing.T) {\n\t\tg := ga.NewWithT(t)\n\t\tdb, err := bolt.Open(path.Join(t.TempDir(), \"./test.bolt\"), 0666, &bolt.Options{Timeout: 1 * time.Second})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t\tdefer db.Close()\n\n\t\terr = db.Update(func(t *bolt.Tx) error {\n\t\t\tcleanupGlobalWithGids(t, nil)\n\t\t\treturn nil\n\t\t})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t})\n\n\tt.Run(\"normal test\", func(t *testing.T) {\n\t\tg := ga.NewWithT(t)\n\t\tdb, err := bolt.Open(path.Join(t.TempDir(), \"./test.bolt\"), 0666, &bolt.Options{Timeout: 1 * time.Second})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t\tdefer db.Close()\n\n\t\t// Initialize data\n\t\terr = db.Update(func(t *bolt.Tx) error {\n\t\t\tbucket, err := t.CreateBucketIfNotExists(bucketGlobal)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tkeys := []string{\"k1\", \"k2\", \"k3\"}\n\t\t\tdatas := []string{\"data1\", \"data2\", \"data3\"}\n\t\t\tfor i := 0; i < len(keys); i++ {\n\t\t\t\terr = bucket.Put([]byte(keys[i]), []byte(datas[i]))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn nil\n\t\t})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\n\t\terr = db.Update(func(t *bolt.Tx) error {\n\t\t\tcleanupGlobalWithGids(t, map[string]struct{}{\n\t\t\t\t\"k1\": {},\n\t\t\t\t\"k2\": {},\n\t\t\t})\n\t\t\treturn nil\n\t\t})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\n\t\tactualGids := []string{}\n\t\terr = db.View(func(t *bolt.Tx) error {\n\t\t\tcursor := t.Bucket(bucketGlobal).Cursor()\n\t\t\tfor k, _ := cursor.First(); k != nil; k, _ = cursor.Next() {\n\t\t\t\tactualGids = append(actualGids, string(k))\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t\tg.Expect(actualGids).To(ga.Equal([]string{\"k3\"}))\n\t})\n}\n\nfunc TestCleanupBranchWithGids(t *testing.T) {\n\tt.Run(\"nil bucket\", func(t *testing.T) {\n\t\tg := ga.NewWithT(t)\n\t\tdb, err := bolt.Open(path.Join(t.TempDir(), \"./test.bolt\"), 0666, &bolt.Options{Timeout: 1 * time.Second})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t\tdefer db.Close()\n\n\t\terr = db.Update(func(t *bolt.Tx) error {\n\t\t\tcleanupBranchWithGids(t, nil)\n\t\t\treturn nil\n\t\t})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t})\n\n\tt.Run(\"normal test\", func(t *testing.T) {\n\t\tg := ga.NewWithT(t)\n\t\tdb, err := bolt.Open(path.Join(t.TempDir(), \"./test.bolt\"), 0666, &bolt.Options{Timeout: 1 * time.Second})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t\tdefer db.Close()\n\n\t\t// Initialize data\n\t\terr = db.Update(func(t *bolt.Tx) error {\n\t\t\tbucket, err := t.CreateBucketIfNotExists(bucketBranches)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tkeys := []string{\"a\", \"gid001\", \"gid002\", \"gid101\", \"gid201\", \"z\"}\n\t\t\tdatas := []storage.TransBranchStore{\n\t\t\t\t{\n\t\t\t\t\tGid: \"a\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tGid: \"gid0\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tGid: \"gid0\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tGid: \"gid1\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tGid: \"gid2\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tGid: \"z\",\n\t\t\t\t},\n\t\t\t}\n\t\t\tfor i := 0; i < len(keys); i++ {\n\t\t\t\terr = bucket.Put([]byte(keys[i]), dtmimp.MustMarshal(datas[i]))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn nil\n\t\t})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\n\t\terr = db.Update(func(t *bolt.Tx) error {\n\t\t\tcleanupBranchWithGids(t, map[string]struct{}{\n\t\t\t\t\"gid0\": {},\n\t\t\t\t\"gid1\": {},\n\t\t\t})\n\t\t\treturn nil\n\t\t})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\n\t\tactualKeys := []string{}\n\t\terr = db.View(func(t *bolt.Tx) error {\n\t\t\tcursor := t.Bucket(bucketBranches).Cursor()\n\t\t\tfor k, _ := cursor.First(); k != nil; k, _ = cursor.Next() {\n\t\t\t\tactualKeys = append(actualKeys, string(k))\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t\tg.Expect(actualKeys).To(ga.Equal([]string{\"a\", \"gid201\", \"z\"}))\n\t})\n}\n\nfunc TestCleanupIndexWithGids(t *testing.T) {\n\tt.Run(\"nil bucket\", func(t *testing.T) {\n\t\tg := ga.NewWithT(t)\n\t\tdb, err := bolt.Open(path.Join(t.TempDir(), \"./test.bolt\"), 0666, &bolt.Options{Timeout: 1 * time.Second})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t\tdefer db.Close()\n\n\t\terr = db.Update(func(t *bolt.Tx) error {\n\t\t\tcleanupIndexWithGids(t, nil)\n\t\t\treturn nil\n\t\t})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t})\n\n\tt.Run(\"normal test\", func(t *testing.T) {\n\t\tg := ga.NewWithT(t)\n\t\tdb, err := bolt.Open(path.Join(t.TempDir(), \"./test.bolt\"), 0666, &bolt.Options{Timeout: 1 * time.Second})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t\tdefer db.Close()\n\n\t\t// Initialize data\n\t\terr = db.Update(func(t *bolt.Tx) error {\n\t\t\tbucket, err := t.CreateBucketIfNotExists(bucketIndex)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tkeys := []string{\"a\", \"0-gid0\", \"1-gid0\", \"2-gid1\", \"3-gid2\", \"z\"}\n\t\t\tdatas := []storage.TransBranchStore{\n\t\t\t\t{\n\t\t\t\t\tGid: \"a\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tGid: \"gid0\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tGid: \"gid0\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tGid: \"gid1\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tGid: \"gid2\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tGid: \"z\",\n\t\t\t\t},\n\t\t\t}\n\t\t\tfor i := 0; i < len(keys); i++ {\n\t\t\t\terr = bucket.Put([]byte(keys[i]), dtmimp.MustMarshal(datas[i]))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn nil\n\t\t})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\n\t\terr = db.Update(func(t *bolt.Tx) error {\n\t\t\tcleanupIndexWithGids(t, map[string]struct{}{\n\t\t\t\t\"gid0\": {},\n\t\t\t\t\"gid1\": {},\n\t\t\t})\n\t\t\treturn nil\n\t\t})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\n\t\tactualKeys := []string{}\n\t\terr = db.View(func(t *bolt.Tx) error {\n\t\t\tcursor := t.Bucket(bucketIndex).Cursor()\n\t\t\tfor k, _ := cursor.First(); k != nil; k, _ = cursor.Next() {\n\t\t\t\tactualKeys = append(actualKeys, string(k))\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t\tg.Expect(err).ToNot(ga.HaveOccurred())\n\t\tg.Expect(actualKeys).To(ga.Equal([]string{\"3-gid2\", \"a\", \"z\"}))\n\t})\n}\n"
  },
  {
    "path": "dtmsvr/storage/redis/redis.go",
    "content": "/*\n * Copyright (c) 2022 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n// package reds implement the storage for reds\npackage redis\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/config\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/redis/go-redis/v9\"\n)\n\n// TODO: optimize this, it's very strange to use pointer to dtmutil.Config\nvar conf = &config.Config\n\n// TODO: optimize this, all function should have context as first parameter\nvar ctx = context.Background()\n\n// Store is the storage with redis, all transaction information will bachend with redis\ntype Store struct {\n}\n\n// Ping execs ping cmd to redis\nfunc (s *Store) Ping() error {\n\t_, err := redisGet().Ping(ctx).Result()\n\treturn err\n}\n\n// PopulateData populates data to redis\nfunc (s *Store) PopulateData(skipDrop bool) {\n\tif !skipDrop {\n\t\t_, err := redisGet().FlushAll(ctx).Result()\n\t\tlogger.Infof(\"call redis flushall. result: %v\", err)\n\t\tdtmimp.PanicIf(err != nil, err)\n\t}\n}\n\n// FindTransGlobalStore finds GlobalTrans data by gid\nfunc (s *Store) FindTransGlobalStore(gid string) *storage.TransGlobalStore {\n\tlogger.Debugf(\"calling FindTransGlobalStore: %s\", gid)\n\tr, err := redisGet().Get(ctx, conf.Store.RedisPrefix+\"_g_\"+gid).Result()\n\tif err == redis.Nil {\n\t\treturn nil\n\t}\n\tdtmimp.E2P(err)\n\ttrans := &storage.TransGlobalStore{}\n\tdtmimp.MustUnmarshalString(r, trans)\n\treturn trans\n}\n\n// ScanTransGlobalStores lists GlobalTrans data\nfunc (s *Store) ScanTransGlobalStores(position *string, limit int64, condition storage.TransGlobalScanCondition) []storage.TransGlobalStore {\n\tlogger.Debugf(\"calling ScanTransGlobalStores: %s %d\", *position, limit)\n\tlid := uint64(0)\n\tif *position != \"\" {\n\t\tlid = uint64(dtmimp.MustAtoi(*position))\n\t}\n\tglobals := []storage.TransGlobalStore{}\n\tredis := redisGet()\n\tfor {\n\t\tlimit -= int64(len(globals))\n\t\tkeys, nextCursor, err := redis.Scan(ctx, lid, conf.Store.RedisPrefix+\"_g_*\", limit).Result()\n\t\tlogger.Debugf(\"calling redis scan: SCAN %d MATCH %s COUNT %d ,scan result: nextCursor:%d keys_len:%d\", lid, conf.Store.RedisPrefix+\"_g_*\", limit, nextCursor, len(keys))\n\n\t\tdtmimp.E2P(err)\n\n\t\tif len(keys) > 0 {\n\t\t\tvalues, err := redis.MGet(ctx, keys...).Result()\n\t\t\tdtmimp.E2P(err)\n\t\t\tfor _, v := range values {\n\t\t\t\tglobal := storage.TransGlobalStore{}\n\t\t\t\tdtmimp.MustUnmarshalString(v.(string), &global)\n\t\t\t\tif (condition.Status == \"\" || global.Status == condition.Status) &&\n\t\t\t\t\t(condition.TransType == \"\" || global.TransType == condition.TransType) &&\n\t\t\t\t\t(condition.CreateTimeStart.IsZero() || global.CreateTime.After(condition.CreateTimeStart)) &&\n\t\t\t\t\t(condition.CreateTimeEnd.IsZero() || global.CreateTime.Before(condition.CreateTimeEnd)) {\n\t\t\t\t\tglobals = append(globals, global)\n\t\t\t\t}\n\t\t\t\t// redis.Scan may return more records than limit\n\t\t\t\tif len(globals) >= int(limit) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlid = nextCursor\n\t\tif len(globals) >= int(limit) || nextCursor == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif lid > 0 {\n\t\t*position = fmt.Sprintf(\"%d\", lid)\n\t} else {\n\t\t*position = \"\"\n\t}\n\treturn globals\n}\n\n// FindBranches finds Branch data by gid\nfunc (s *Store) FindBranches(gid string) []storage.TransBranchStore {\n\tlogger.Debugf(\"calling FindBranches: %s\", gid)\n\tsa, err := redisGet().LRange(ctx, conf.Store.RedisPrefix+\"_b_\"+gid, 0, -1).Result()\n\tdtmimp.E2P(err)\n\tbranches := make([]storage.TransBranchStore, len(sa))\n\tfor k, v := range sa {\n\t\tdtmimp.MustUnmarshalString(v, &branches[k])\n\t}\n\treturn branches\n}\n\n// UpdateBranches updates branches info\nfunc (s *Store) UpdateBranches(branches []storage.TransBranchStore, updates []string) (int, error) {\n\treturn 0, nil // not implemented\n}\n\ntype argList struct {\n\tKeys []string      // 1 global trans, 2 branches, 3 indices, 4 status\n\tList []interface{} // 1 redis prefix, 2 data expire\n}\n\nfunc newArgList() *argList {\n\ta := &argList{}\n\treturn a.AppendRaw(conf.Store.RedisPrefix).AppendObject(conf.Store.DataExpire)\n}\n\nfunc (a *argList) AppendGid(gid string) *argList {\n\ta.Keys = append(a.Keys, conf.Store.RedisPrefix+\"_g_\"+gid)\n\ta.Keys = append(a.Keys, conf.Store.RedisPrefix+\"_b_\"+gid)\n\ta.Keys = append(a.Keys, conf.Store.RedisPrefix+\"_u\")\n\ta.Keys = append(a.Keys, conf.Store.RedisPrefix+\"_s_\"+gid)\n\treturn a\n}\n\nfunc (a *argList) AppendRaw(v interface{}) *argList {\n\ta.List = append(a.List, v)\n\treturn a\n}\n\nfunc (a *argList) AppendObject(v interface{}) *argList {\n\treturn a.AppendRaw(dtmimp.MustMarshalString(v))\n}\n\nfunc (a *argList) AppendBranches(branches []storage.TransBranchStore) *argList {\n\tfor _, b := range branches {\n\t\ta.AppendRaw(dtmimp.MustMarshalString(b))\n\t}\n\treturn a\n}\n\nfunc handleRedisResult(ret interface{}, err error) (string, error) {\n\tlogger.Debugf(\"result is: '%v', err: '%v'\", ret, err)\n\tif err != nil && err != redis.Nil {\n\t\treturn \"\", err\n\t}\n\ts, _ := ret.(string)\n\terr = map[string]error{\n\t\t\"NOT_FOUND\":       storage.ErrNotFound,\n\t\t\"UNIQUE_CONFLICT\": storage.ErrUniqueConflict,\n\t}[s]\n\treturn s, err\n}\n\nfunc callLua(a *argList, lua string) (string, error) {\n\tlogger.Debugf(\"calling lua. args: %v\\nlua:%s\", a, lua)\n\tret, err := redisGet().Eval(ctx, lua, a.Keys, a.List...).Result()\n\treturn handleRedisResult(ret, err)\n}\n\n// MaySaveNewTrans creates a new trans\nfunc (s *Store) MaySaveNewTrans(global *storage.TransGlobalStore, branches []storage.TransBranchStore) error {\n\ta := newArgList().\n\t\tAppendGid(global.Gid).\n\t\tAppendObject(global).\n\t\tAppendRaw(global.NextCronTime.Unix()).\n\t\tAppendRaw(global.Gid).\n\t\tAppendRaw(global.Status).\n\t\tAppendBranches(branches)\n\tglobal.Steps = nil\n\tglobal.Payloads = nil\n\t_, err := callLua(a, `-- MaySaveNewTrans\nlocal g = redis.call('GET', KEYS[1])\nif g ~= false then\n\treturn 'UNIQUE_CONFLICT'\nend\n\nredis.call('SET', KEYS[1], ARGV[3], 'EX', ARGV[2])\nredis.call('SET', KEYS[4], ARGV[6], 'EX', ARGV[2])\nredis.call('ZADD', KEYS[3], ARGV[4], ARGV[5])\nfor k = 7, table.getn(ARGV) do\n\tredis.call('RPUSH', KEYS[2], ARGV[k])\nend\nredis.call('EXPIRE', KEYS[2], ARGV[2])\n`)\n\treturn err\n}\n\n// LockGlobalSaveBranches creates branches\nfunc (s *Store) LockGlobalSaveBranches(gid string, status string, branches []storage.TransBranchStore, branchStart int) {\n\targs := newArgList().\n\t\tAppendGid(gid).\n\t\tAppendRaw(status).\n\t\tAppendRaw(branchStart).\n\t\tAppendBranches(branches)\n\t_, err := callLua(args, `-- LockGlobalSaveBranches\nlocal old = redis.call('GET', KEYS[4])\nif old ~= ARGV[3] then\n\treturn 'NOT_FOUND'\nend\nlocal start = ARGV[4]\n-- check duplicates for workflow\nif start == \"-1\" then\n\tlocal t = cjson.decode(ARGV[5])\n\tlocal bs = redis.call('LRANGE', KEYS[2], 0, -1)\n\tfor i = 1, table.getn(bs) do\n\t\tlocal c = cjson.decode(bs[i])\n\t\tif t['branch_id'] == c['branch_id'] and t['op'] == c['op'] then\n\t\t\treturn 'UNIQUE_CONFLICT'\n\t\tend\n\tend\nend\nfor k = 5, table.getn(ARGV) do\n\tif start == \"-1\" then\n\t\tredis.call('RPUSH', KEYS[2], ARGV[k])\n\telse\n\t\tredis.call('LSET', KEYS[2], start+k-5, ARGV[k])\n\tend\nend\nredis.call('EXPIRE', KEYS[2], ARGV[2])\n\t`)\n\tdtmimp.E2P(err)\n}\n\n// ChangeGlobalStatus changes global trans status\nfunc (s *Store) ChangeGlobalStatus(global *storage.TransGlobalStore, newStatus string, updates []string, finished bool) {\n\told := global.Status\n\tglobal.Status = newStatus\n\targs := newArgList().\n\t\tAppendGid(global.Gid).\n\t\tAppendObject(global).\n\t\tAppendRaw(old).\n\t\tAppendRaw(finished).\n\t\tAppendRaw(global.Gid).\n\t\tAppendRaw(newStatus).\n\t\tAppendObject(conf.Store.FinishedDataExpire)\n\t_, err := callLua(args, `-- ChangeGlobalStatus\nlocal old = redis.call('GET', KEYS[4])\nif old ~= ARGV[4] then\n  return 'NOT_FOUND'\nend\nredis.call('SET', KEYS[1],  ARGV[3], 'EX', ARGV[2])\nredis.call('SET', KEYS[4],  ARGV[7], 'EX', ARGV[2])\nif ARGV[5] == '1' then\n\tredis.call('ZREM', KEYS[3], ARGV[6])\n\tredis.call('EXPIRE', KEYS[1], ARGV[8])\n\tredis.call('EXPIRE', KEYS[2], ARGV[8])\n\tredis.call('EXPIRE', KEYS[4], ARGV[8])\nend\n`)\n\tdtmimp.E2P(err)\n}\n\n// LockOneGlobalTrans finds GlobalTrans\nfunc (s *Store) LockOneGlobalTrans(expireIn time.Duration) *storage.TransGlobalStore {\n\texpired := time.Now().Add(expireIn).Unix()\n\tnext := time.Now().Add(time.Duration(conf.RetryInterval) * time.Second).Unix()\n\targs := newArgList().AppendGid(\"\").AppendRaw(expired).AppendRaw(next)\n\tlua := `-- LockOneGlobalTrans\nlocal r = redis.call('ZRANGE', KEYS[3], 0, 0, 'WITHSCORES')\nlocal gid = r[1]\nif gid == nil then\n\treturn 'NOT_FOUND'\nend\n\nif tonumber(r[2]) > tonumber(ARGV[3]) then\n\treturn 'NOT_FOUND'\nend\nredis.call('ZADD', KEYS[3], ARGV[4], gid)\nreturn gid\n`\n\tfor {\n\t\tr, err := callLua(args, lua)\n\t\tif errors.Is(err, storage.ErrNotFound) {\n\t\t\treturn nil\n\t\t}\n\t\tdtmimp.E2P(err)\n\t\tglobal := s.FindTransGlobalStore(r)\n\t\tif global != nil {\n\t\t\treturn global\n\t\t}\n\t}\n}\n\n// ResetCronTime reset nextCronTime\n// unfinished transactions need to be retried as soon as possible after business downtime is recovered\nfunc (s *Store) ResetCronTime(after time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error) {\n\tnext := time.Now().Unix()\n\ttimeoutTimestamp := time.Now().Add(after).Unix()\n\targs := newArgList().AppendGid(\"\").AppendRaw(timeoutTimestamp).AppendRaw(next).AppendRaw(limit)\n\tlua := `-- ResetCronTime\nlocal r = redis.call('ZRANGEBYSCORE', KEYS[3], ARGV[3], '+inf', 'LIMIT', 0, ARGV[5]+1)\nlocal i = 0\nfor score,gid in pairs(r) do\n\tif i == tonumber(ARGV[5]) then\n\t\ti = i + 1\n\t\tbreak\n\tend\n\tredis.call('ZADD', KEYS[3], ARGV[4], gid)\n\ti = i + 1\nend\nreturn tostring(i)\n`\n\tr := \"\"\n\tr, err = callLua(args, lua)\n\tdtmimp.E2P(err)\n\tsucceedCount = int64(dtmimp.MustAtoi(r))\n\tif succeedCount > limit {\n\t\thasRemaining = true\n\t\tsucceedCount = limit\n\t}\n\treturn\n}\n\n// ResetTransGlobalCronTime reset nextCronTime of one global trans.\nfunc (s *Store) ResetTransGlobalCronTime(global *storage.TransGlobalStore) error {\n\tnow := dtmutil.GetNextTime(0)\n\tglobal.NextCronTime = now\n\tglobal.UpdateTime = now\n\tkey := conf.Store.RedisPrefix + \"_g_\" + global.Gid\n\t_, err := redisGet().Set(ctx, key, dtmimp.MustMarshalString(global), time.Duration(conf.Store.DataExpire)*time.Second).Result()\n\treturn err\n}\n\n// TouchCronTime updates cronTime\nfunc (s *Store) TouchCronTime(global *storage.TransGlobalStore, nextCronInterval int64, nextCronTime *time.Time) {\n\tglobal.UpdateTime = dtmutil.GetNextTime(0)\n\tglobal.NextCronTime = nextCronTime\n\tglobal.NextCronInterval = nextCronInterval\n\targs := newArgList().\n\t\tAppendGid(global.Gid).\n\t\tAppendObject(global).\n\t\tAppendRaw(global.NextCronTime.Unix()).\n\t\tAppendRaw(global.Status).\n\t\tAppendRaw(global.Gid)\n\t_, err := callLua(args, `-- TouchCronTime\nlocal old = redis.call('GET', KEYS[4])\nif old ~= ARGV[5] then\n\treturn 'NOT_FOUND'\nend\nredis.call('ZADD', KEYS[3], ARGV[4], ARGV[6])\nredis.call('SET', KEYS[1], ARGV[3], 'EX', ARGV[2])\n\t`)\n\tdtmimp.E2P(err)\n}\n\n// ScanKV lists KV pairs\nfunc (s *Store) ScanKV(cat string, position *string, limit int64) []storage.KVStore {\n\tlogger.Debugf(\"calling ScanKV: %s %s %d\", cat, *position, limit)\n\tlid := uint64(0)\n\tif *position != \"\" {\n\t\tlid = uint64(dtmimp.MustAtoi(*position))\n\t}\n\n\tkvs := []storage.KVStore{}\n\tredis := redisGet()\n\tfor {\n\t\tlimit -= int64(len(kvs))\n\t\tkeys, nextCursor, err := redis.Scan(ctx, lid, conf.Store.RedisPrefix+\"_kv_\"+cat+\"_*\", limit).Result()\n\t\tlogger.Debugf(\"calling redis scan: SCAN %d MATCH %s COUNT %d ,scan result: nextCursor:%d keys_len:%d\", lid, conf.Store.RedisPrefix+\"_kv_\"+cat+\"_*\", limit, nextCursor, len(keys))\n\t\tdtmimp.E2P(err)\n\t\tif len(keys) > 0 {\n\t\t\tvalues, err := redis.MGet(ctx, keys...).Result()\n\t\t\tdtmimp.E2P(err)\n\t\t\tlogger.Debugf(\"keys: %s values: %s\", dtmimp.MustMarshalString(keys), dtmimp.MustMarshalString(values))\n\t\t\tfor _, v := range values {\n\t\t\t\tif v == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tkv := storage.KVStore{}\n\t\t\t\tdtmimp.MustUnmarshalString(v.(string), &kv)\n\t\t\t\tkvs = append(kvs, kv)\n\t\t\t}\n\t\t}\n\n\t\tlid = nextCursor\n\t\t// for redis, `count` in `scan` command is only a hint, may return more than `count` items\n\t\tif len(kvs) >= int(limit) || nextCursor == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif lid > 0 {\n\t\t*position = fmt.Sprintf(\"%d\", lid)\n\t} else {\n\t\t*position = \"\"\n\t}\n\treturn kvs\n}\n\n// FindKV finds key-value pairs\nfunc (s *Store) FindKV(cat, key string) []storage.KVStore {\n\tvar keys []string\n\tpattern := conf.Store.RedisPrefix + \"_kv_\"\n\tif cat != \"\" {\n\t\tpattern += cat + \"_\"\n\t}\n\tif key != \"\" {\n\t\tkeys = []string{pattern + key}\n\t} else {\n\t\tlid := uint64(0)\n\t\tr := redisGet().Scan(ctx, lid, pattern+\"*\", int64(-1))\n\t\tdtmimp.E2P(r.Err())\n\t\tkeys, _ = r.Val()\n\t}\n\n\tkvs := []storage.KVStore{}\n\tif len(keys) <= 0 {\n\t\treturn nil\n\t}\n\tvalues, err := redisGet().MGet(ctx, keys...).Result()\n\tdtmimp.E2P(err)\n\tfor _, v := range values {\n\t\tif v == nil {\n\t\t\tcontinue\n\t\t}\n\t\tkv := storage.KVStore{}\n\t\tdtmimp.MustUnmarshalString(v.(string), &kv)\n\t\tkvs = append(kvs, kv)\n\t}\n\treturn kvs\n}\n\n// UpdateKV updates key-value pair\nfunc (s *Store) UpdateKV(kv *storage.KVStore) error {\n\tnow := time.Now()\n\tkv.UpdateTime = &now\n\toldVersion := kv.Version\n\tkv.Version = oldVersion + 1\n\n\tredisKey := fmt.Sprintf(\"%s_kv_%s_%s\", conf.Store.RedisPrefix, kv.Cat, kv.K)\n\targs := &argList{}\n\targs.Keys = append(args.Keys, redisKey)\n\targs.AppendRaw(oldVersion)\n\targs.AppendObject(kv)\n\t_, err := callLua(args, `-- UpdateKV\nlocal oldJson = redis.call('GET', KEYS[1])\nif oldJson == false then\n\treturn 'NOT_FOUND'\nend\nlocal old = cjson.decode(oldJson)\nif tostring(old.version) == ARGV[1] then\n\tredis.call('SET', KEYS[1], ARGV[2])\nelse \n\treturn 'NOT_FOUND'\nend\n`)\n\treturn err\n}\n\n// DeleteKV deletes key-value pair\nfunc (s *Store) DeleteKV(cat, key string) error {\n\taffected, err := redisGet().Del(ctx, fmt.Sprintf(\"%s_kv_%s_%s\", conf.Store.RedisPrefix, cat, key)).Result()\n\tif err == nil && affected == 0 {\n\t\treturn storage.ErrNotFound\n\t}\n\treturn err\n}\n\n// CreateKV creates key-value pair\nfunc (s *Store) CreateKV(cat, key, value string) error {\n\tnow := time.Now()\n\tkv := &storage.KVStore{\n\t\tModelBase: dtmutil.ModelBase{\n\t\t\tCreateTime: &now,\n\t\t\tUpdateTime: &now,\n\t\t},\n\t\tCat:     cat,\n\t\tK:       key,\n\t\tV:       value,\n\t\tVersion: 1,\n\t}\n\tredisKey := fmt.Sprintf(\"%s_kv_%s_%s\", conf.Store.RedisPrefix, kv.Cat, kv.K)\n\targs := &argList{}\n\targs.Keys = append(args.Keys, redisKey)\n\targs.AppendObject(kv)\n\t_, err := callLua(args, `-- CreateKV\nlocal key = redis.call('GET', KEYS[1])\nif key ~= false then\n\treturn 'UNIQUE_CONFLICT'\nend\nredis.call('SET', KEYS[1], ARGV[1])\n`)\n\treturn err\n}\n\nvar (\n\trdb  redis.Cmdable\n\tonce sync.Once\n)\n\nfunc redisGet() redis.Cmdable {\n\tonce.Do(func() {\n\t\tlogger.Debugf(\"connecting to redis: %v\", conf.Store)\n\t\tendpoints := strings.Split(conf.Store.Host, \",\")\n\t\tif len(endpoints) == 1 {\n\t\t\trdb = redis.NewClient(&redis.Options{\n\t\t\t\tAddr:     fmt.Sprintf(\"%s:%d\", conf.Store.Host, conf.Store.Port),\n\t\t\t\tUsername: conf.Store.User,\n\t\t\t\tPassword: conf.Store.Password,\n\t\t\t})\n\t\t} else {\n\t\t\trdb = redis.NewClusterClient(&redis.ClusterOptions{\n\t\t\t\tAddrs:    endpoints,\n\t\t\t\tUsername: conf.Store.User,\n\t\t\t\tPassword: conf.Store.Password,\n\t\t\t})\n\t\t}\n\t})\n\treturn rdb\n}\n"
  },
  {
    "path": "dtmsvr/storage/registry/factory.go",
    "content": "package registry\n\nimport (\n\t\"sync\"\n\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage\"\n)\n\n// SingletonFactory is the factory to build store in SINGLETON pattern.\ntype SingletonFactory struct {\n\tonce sync.Once\n\n\tstore storage.Store\n\n\tcreatorFunction func() storage.Store\n}\n\n// GetStorage implement the StorageFactory.GetStorage\nfunc (f *SingletonFactory) GetStorage() storage.Store {\n\tf.once.Do(func() {\n\t\tf.store = f.creatorFunction()\n\t})\n\n\treturn f.store\n}\n"
  },
  {
    "path": "dtmsvr/storage/registry/registry.go",
    "content": "package registry\n\nimport (\n\t\"time\"\n\n\t\"github.com/dtm-labs/logger\"\n\n\t\"github.com/dtm-labs/dtm/dtmsvr/config\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage/boltdb\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage/redis\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage/sql\"\n)\n\nvar conf = &config.Config\n\n// StorageFactory is factory to get storage instance.\ntype StorageFactory interface {\n\t// GetStorage will return the Storage instance.\n\tGetStorage() storage.Store\n}\n\nvar sqlFac = &SingletonFactory{\n\tcreatorFunction: func() storage.Store {\n\t\treturn &sql.Store{}\n\t},\n}\n\nvar storeFactorys = map[string]StorageFactory{\n\t\"boltdb\": &SingletonFactory{\n\t\tcreatorFunction: func() storage.Store {\n\t\t\treturn boltdb.NewStore(conf.Store.DataExpire, conf.RetryInterval)\n\t\t},\n\t},\n\t\"redis\": &SingletonFactory{\n\t\tcreatorFunction: func() storage.Store {\n\t\t\treturn &redis.Store{}\n\t\t},\n\t},\n\t\"mysql\":     sqlFac,\n\t\"postgres\":  sqlFac,\n\t\"sqlserver\": sqlFac,\n}\n\n// GetStore returns storage.Store\nfunc GetStore() storage.Store {\n\treturn storeFactorys[conf.Store.Driver].GetStorage()\n}\n\n// WaitStoreUp wait for db to go up\nfunc WaitStoreUp() {\n\tfor err := GetStore().Ping(); err != nil; err = GetStore().Ping() {\n\t\tlogger.Infof(\"wait store up: %v\", err)\n\t\ttime.Sleep(3 * time.Second)\n\t}\n}\n"
  },
  {
    "path": "dtmsvr/storage/sql/sql.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n// package boltdb implement the storage for sql database\npackage sql\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/config\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/lithammer/shortuuid/v3\"\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n)\n\nvar conf = &config.Config\n\n// Store implements storage.Store, and storage with db\ntype Store struct {\n}\n\n// Ping execs ping cmd to db\nfunc (s *Store) Ping() error {\n\tdb, err := dtmimp.StandaloneDB(conf.Store.GetDBConf())\n\tdtmimp.E2P(err)\n\t_, err = db.Exec(\"select 1\")\n\treturn err\n}\n\n// PopulateData populates data to db\nfunc (s *Store) PopulateData(skipDrop bool) {\n\tfile := fmt.Sprintf(\"%s/dtmsvr.storage.%s.sql\", dtmutil.GetSQLDir(), conf.Store.Driver)\n\tdtmutil.RunSQLScript(conf.Store.GetDBConf(), file, skipDrop)\n}\n\n// FindTransGlobalStore finds GlobalTrans data by gid\nfunc (s *Store) FindTransGlobalStore(gid string) *storage.TransGlobalStore {\n\ttrans := &storage.TransGlobalStore{}\n\tdbr := dbGet().Model(trans).Where(\"gid=?\", gid).First(trans)\n\tif dbr.Error == gorm.ErrRecordNotFound {\n\t\treturn nil\n\t}\n\tdtmimp.E2P(dbr.Error)\n\treturn trans\n}\n\n// ScanTransGlobalStores lists GlobalTrans data\nfunc (s *Store) ScanTransGlobalStores(position *string, limit int64, condition storage.TransGlobalScanCondition) []storage.TransGlobalStore {\n\tglobals := []storage.TransGlobalStore{}\n\tlid := math.MaxInt64\n\tif *position != \"\" {\n\t\tlid = dtmimp.MustAtoi(*position)\n\t}\n\tquery := dbGet().Must().Where(\"id < ?\", lid)\n\tif condition.Status != \"\" {\n\t\tquery = query.Where(\"status = ?\", condition.Status)\n\t}\n\tif condition.TransType != \"\" {\n\t\tquery = query.Where(\"trans_type = ?\", condition.TransType)\n\t}\n\tif !condition.CreateTimeStart.IsZero() {\n\t\tquery = query.Where(\"create_time >= ?\", condition.CreateTimeStart)\n\t}\n\tif !condition.CreateTimeEnd.IsZero() {\n\t\tquery = query.Where(\"create_time <= ?\", condition.CreateTimeEnd)\n\t}\n\n\tdbr := query.Order(\"id desc\").Limit(int(limit)).Find(&globals)\n\n\tif dbr.RowsAffected < limit {\n\t\t*position = \"\"\n\t} else {\n\t\t*position = fmt.Sprintf(\"%d\", globals[len(globals)-1].ID)\n\t}\n\treturn globals\n}\n\n// FindBranches finds Branch data by gid\nfunc (s *Store) FindBranches(gid string) []storage.TransBranchStore {\n\tbranches := []storage.TransBranchStore{}\n\tdbGet().Must().Where(\"gid=?\", gid).Order(\"id asc\").Find(&branches)\n\treturn branches\n}\n\n// UpdateBranches update branches info\nfunc (s *Store) UpdateBranches(branches []storage.TransBranchStore, updates []string) (int, error) {\n\tdb := dbGet().Clauses(clause.OnConflict{\n\t\tOnConstraint: \"gid_branch_uniq\",\n\t\tDoUpdates:    clause.AssignmentColumns(updates),\n\t}).Create(branches)\n\treturn int(db.RowsAffected), db.Error\n}\n\n// LockGlobalSaveBranches creates branches\nfunc (s *Store) LockGlobalSaveBranches(gid string, status string, branches []storage.TransBranchStore, branchStart int) {\n\terr := dbGet().Transaction(func(tx *gorm.DB) error {\n\t\tg := &storage.TransGlobalStore{}\n\t\tvar dbr *gorm.DB\n\t\t// sqlserver sql should be: SELECT * FROM \"trans_global\" with(RowLock,UpdLock) ,but gorm generates \"FOR UPDATE\" at the back, raw sql instead.\n\t\tif conf.Store.Driver == config.SQLServer {\n\t\t\tdbr = tx.Raw(\"SELECT * FROM trans_global with(RowLock,UpdLock) WHERE gid=? and status=? ORDER BY id OFFSET 0 ROW FETCH NEXT 1 ROWS ONLY \", gid, status).First(g)\n\t\t} else {\n\t\t\tdbr = tx.Clauses(clause.Locking{Strength: \"UPDATE\"}).Model(g).Where(\"gid=? and status=?\", gid, status).First(g)\n\t\t}\n\t\tif dbr.Error == nil {\n\t\t\tif branchStart == -1 {\n\t\t\t\tdbr = tx.Create(branches)\n\t\t\t} else {\n\t\t\t\tdbr = tx.Save(branches)\n\t\t\t}\n\t\t}\n\t\treturn wrapError(dbr.Error)\n\t})\n\tdtmimp.E2P(err)\n}\n\n// MaySaveNewTrans creates a new trans\nfunc (s *Store) MaySaveNewTrans(global *storage.TransGlobalStore, branches []storage.TransBranchStore) error {\n\treturn dbGet().Transaction(func(db1 *gorm.DB) error {\n\t\tdb := &dtmutil.DB{DB: db1}\n\t\tdbr := db.Must().Clauses(clause.OnConflict{\n\t\t\tDoNothing: true,\n\t\t}).Create(global)\n\t\tif dbr.RowsAffected <= 0 { // not a new trans, return\n\t\t\treturn storage.ErrUniqueConflict\n\t\t}\n\t\tif len(branches) > 0 {\n\t\t\tdb.Must().Clauses(clause.OnConflict{\n\t\t\t\tDoNothing: true,\n\t\t\t}).Create(&branches)\n\t\t}\n\t\treturn nil\n\t})\n}\n\n// ChangeGlobalStatus changes global trans status\nfunc (s *Store) ChangeGlobalStatus(global *storage.TransGlobalStore, newStatus string, updates []string, finished bool) {\n\told := global.Status\n\tglobal.Status = newStatus\n\tdbr := dbGet().Must().Model(global).Where(\"status=? and gid=?\", old, global.Gid).Select(updates).Updates(global)\n\tif dbr.RowsAffected == 0 {\n\t\tdtmimp.E2P(storage.ErrNotFound)\n\t}\n}\n\n// TouchCronTime updates cronTime\nfunc (s *Store) TouchCronTime(global *storage.TransGlobalStore, nextCronInterval int64, nextCronTime *time.Time) {\n\tglobal.UpdateTime = dtmutil.GetNextTime(0)\n\tglobal.NextCronTime = nextCronTime\n\tglobal.NextCronInterval = nextCronInterval\n\tdbGet().Must().Model(global).Where(\"status=? and gid=?\", global.Status, global.Gid).\n\t\tSelect([]string{\"next_cron_time\", \"update_time\", \"next_cron_interval\"}).Updates(global)\n}\n\n// LockOneGlobalTrans finds GlobalTrans\nfunc (s *Store) LockOneGlobalTrans(expireIn time.Duration) *storage.TransGlobalStore {\n\tdb := dbGet()\n\towner := shortuuid.New()\n\tnextCronTime := getTimeStr(int64(expireIn / time.Second))\n\twhere := fmt.Sprintf(`next_cron_time < '%s' and status in ('prepared', 'aborting', 'submitted')`, nextCronTime)\n\n\tssql := map[string]string{\n\t\tdtmimp.DBTypeMysql:     fmt.Sprintf(`select id from trans_global where %s order by rand() limit 1`, where),\n\t\tdtmimp.DBTypePostgres:  fmt.Sprintf(`select id from trans_global where %s order by random() limit 1`, where),\n\t\tdtmimp.DBTypeSQLServer: fmt.Sprintf(`select top 1 id from trans_global where %s order by rand()`, where),\n\t}[conf.Store.Driver]\n\tvar id int64\n\terr := db.ToSQLDB().QueryRow(ssql).Scan(&id)\n\tif errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil\n\t}\n\tdtmimp.PanicIf(err != nil, err)\n\n\tsql := fmt.Sprintf(`UPDATE trans_global SET update_time='%s',next_cron_time='%s', owner='%s' WHERE id=%d and %s`,\n\t\tgetTimeStr(0),\n\t\tgetTimeStr(conf.RetryInterval),\n\t\towner,\n\t\tid,\n\t\twhere)\n\taffected, err := dtmimp.DBExec(conf.Store.Driver, db.ToSQLDB(), sql)\n\n\tdtmimp.PanicIf(err != nil, err)\n\tif affected == 0 {\n\t\treturn nil\n\t}\n\tglobal := &storage.TransGlobalStore{}\n\tdb.Must().Where(\"owner=?\", owner).First(global)\n\treturn global\n}\n\n// ResetCronTime reset nextCronTime\n// unfinished transactions need to be retried as soon as possible after business downtime is recovered\nfunc (s *Store) ResetCronTime(after time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error) {\n\tnextCronTime := getTimeStr(int64(after / time.Second))\n\twhere := map[string]string{\n\t\tdtmimp.DBTypeMysql:     fmt.Sprintf(`next_cron_time > '%s' and status in ('prepared', 'aborting', 'submitted') limit %d`, nextCronTime, limit),\n\t\tdtmimp.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),\n\t\tdtmimp.DBTypeSQLServer: fmt.Sprintf(`id in (select top %d id from trans_global where next_cron_time > '%s' and status in ('prepared', 'aborting', 'submitted') )`, limit, nextCronTime),\n\t}[conf.Store.Driver]\n\n\tsql := fmt.Sprintf(`UPDATE trans_global SET update_time='%s',next_cron_time='%s' WHERE %s`,\n\t\tgetTimeStr(0),\n\t\tgetTimeStr(0),\n\t\twhere)\n\taffected, err := dtmimp.DBExec(conf.Store.Driver, dbGet().ToSQLDB(), sql)\n\treturn affected, affected == limit, err\n}\n\n// ResetTransGlobalCronTime reset nextCronTime of one global trans.\nfunc (s *Store) ResetTransGlobalCronTime(global *storage.TransGlobalStore) error {\n\tnow := getTimeStr(0)\n\tsql := fmt.Sprintf(`UPDATE trans_global SET update_time='%s',next_cron_time='%s' WHERE gid = '%s'`,\n\t\tnow,\n\t\tnow,\n\t\tglobal.Gid)\n\t_, err := dtmimp.DBExec(conf.Store.Driver, dbGet().ToSQLDB(), sql)\n\treturn err\n}\n\n// ScanKV lists KV pairs\nfunc (s *Store) ScanKV(cat string, position *string, limit int64) []storage.KVStore {\n\tkvs := []storage.KVStore{}\n\tlid := math.MaxInt64\n\tif *position != \"\" {\n\t\tlid = dtmimp.MustAtoi(*position)\n\t}\n\tdbr := dbGet().Must().Where(\"cat = ? and id < ?\", cat, lid).Order(\"id desc\").Limit(int(limit)).Find(&kvs)\n\tif dbr.RowsAffected < limit {\n\t\t*position = \"\"\n\t} else {\n\t\t*position = fmt.Sprintf(\"%d\", kvs[len(kvs)-1].ID)\n\t}\n\treturn kvs\n}\n\n// FindKV finds key-value pairs\nfunc (s *Store) FindKV(cat, key string) []storage.KVStore {\n\tkvs := []storage.KVStore{}\n\tdb := dbGet().Model(&storage.KVStore{})\n\tif cat != \"\" {\n\t\tdb = db.Where(\"cat=?\", cat)\n\t}\n\tif key != \"\" {\n\t\tdb = db.Where(\"k=?\", key)\n\t}\n\tdb.Find(&kvs)\n\treturn kvs\n}\n\n// UpdateKV updates key-value pair\nfunc (s *Store) UpdateKV(kv *storage.KVStore) error {\n\tnow := time.Now()\n\tkv.UpdateTime = &now\n\toldVersion := kv.Version\n\tkv.Version = oldVersion + 1\n\tdbr := dbGet().Model(&storage.KVStore{}).Where(\"id=? and version=?\", kv.ID, oldVersion).\n\t\tUpdates(kv)\n\tif dbr.Error == nil && dbr.RowsAffected == 0 {\n\t\treturn storage.ErrNotFound\n\t}\n\treturn dbr.Error\n}\n\n// DeleteKV deletes key-value pair\nfunc (s *Store) DeleteKV(cat, key string) error {\n\tdbr := dbGet().Where(\"cat=? and k=?\", cat, key).Delete(&storage.KVStore{})\n\tif dbr.Error == nil && dbr.RowsAffected == 0 {\n\t\treturn storage.ErrNotFound\n\t}\n\treturn dbr.Error\n}\n\n// CreateKV creates key-value pair\nfunc (s *Store) CreateKV(cat, key, value string) error {\n\tnow := time.Now()\n\tkv := &storage.KVStore{\n\t\tModelBase: dtmutil.ModelBase{\n\t\t\tCreateTime: &now,\n\t\t\tUpdateTime: &now,\n\t\t},\n\t\tCat:     cat,\n\t\tK:       key,\n\t\tV:       value,\n\t\tVersion: 1,\n\t}\n\tdbr := dbGet().Clauses(clause.OnConflict{\n\t\tDoNothing: true,\n\t}).Create(kv)\n\tif dbr.Error == nil && dbr.RowsAffected == 0 {\n\t\treturn storage.ErrUniqueConflict\n\t}\n\treturn dbr.Error\n}\n\n// SetDBConn sets db conn pool\nfunc SetDBConn(db *gorm.DB) {\n\tsqldb, _ := db.DB()\n\tsqldb.SetMaxOpenConns(int(conf.Store.MaxOpenConns))\n\tsqldb.SetMaxIdleConns(int(conf.Store.MaxIdleConns))\n\tsqldb.SetConnMaxLifetime(time.Duration(conf.Store.ConnMaxLifeTime) * time.Minute)\n}\n\nfunc dbGet() *dtmutil.DB {\n\treturn dtmutil.DbGet(conf.Store.GetDBConf(), SetDBConn)\n}\n\nfunc wrapError(err error) error {\n\tif err == gorm.ErrRecordNotFound {\n\t\treturn storage.ErrNotFound\n\t}\n\tdtmimp.E2P(err)\n\treturn err\n}\n\nfunc getTimeStr(afterSecond int64) string {\n\tif conf.Store.Driver == config.SQLServer {\n\t\treturn dtmutil.GetNextTime(afterSecond).Format(time.RFC3339)\n\t}\n\treturn dtmutil.GetNextTime(afterSecond).Format(\"2006-01-02 15:04:05\")\n}\n"
  },
  {
    "path": "dtmsvr/storage/store.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage storage\n\nimport (\n\t\"errors\"\n\t\"time\"\n)\n\n// ErrNotFound defines the query item is not found in storage implement.\nvar ErrNotFound = errors.New(\"storage: NotFound\")\n\n// ErrUniqueConflict defines the item is conflict with unique key in storage implement.\nvar ErrUniqueConflict = errors.New(\"storage: UniqueKeyConflict\")\n\n// Store defines storage relevant interface\ntype Store interface {\n\tPing() error\n\tPopulateData(skipDrop bool)\n\tFindTransGlobalStore(gid string) *TransGlobalStore\n\tScanTransGlobalStores(position *string, limit int64, condition TransGlobalScanCondition) []TransGlobalStore\n\tFindBranches(gid string) []TransBranchStore\n\tUpdateBranches(branches []TransBranchStore, updates []string) (int, error)\n\tLockGlobalSaveBranches(gid string, status string, branches []TransBranchStore, branchStart int)\n\tMaySaveNewTrans(global *TransGlobalStore, branches []TransBranchStore) error\n\tChangeGlobalStatus(global *TransGlobalStore, newStatus string, updates []string, finished bool)\n\tTouchCronTime(global *TransGlobalStore, nextCronInterval int64, nextCronTime *time.Time)\n\tLockOneGlobalTrans(expireIn time.Duration) *TransGlobalStore\n\tResetCronTime(after time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error)\n\tResetTransGlobalCronTime(global *TransGlobalStore) error\n\tScanKV(cat string, position *string, limit int64) []KVStore\n\tFindKV(cat, key string) []KVStore\n\tUpdateKV(kv *KVStore) error\n\tDeleteKV(cat, key string) error\n\tCreateKV(cat, key, value string) error\n}\n\n// TransGlobalScanCondition contains filter options to scan global trans.\ntype TransGlobalScanCondition struct {\n\tStatus          string\n\tTransType       string\n\tCreateTimeStart time.Time\n\tCreateTimeEnd   time.Time\n}\n"
  },
  {
    "path": "dtmsvr/storage/trans.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage storage\n\nimport (\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n)\n\n// TransGlobalExt defines Header info\ntype TransGlobalExt struct {\n\tHeaders map[string]string `json:\"headers,omitempty\" gorm:\"-\"`\n}\n\n// TransGlobalStore defines GlobalStore storage info\ntype TransGlobalStore struct {\n\tdtmutil.ModelBase\n\tGid              string              `json:\"gid,omitempty\"`\n\tTransType        string              `json:\"trans_type,omitempty\"`\n\tSteps            []map[string]string `json:\"steps,omitempty\" gorm:\"-\"`\n\tPayloads         []string            `json:\"payloads,omitempty\" gorm:\"-\"`\n\tBinPayloads      [][]byte            `json:\"-\" gorm:\"-\"`\n\tStatus           string              `json:\"status,omitempty\"`\n\tQueryPrepared    string              `json:\"query_prepared,omitempty\"`\n\tProtocol         string              `json:\"protocol,omitempty\"`\n\tFinishTime       *time.Time          `json:\"finish_time,omitempty\"`\n\tRollbackTime     *time.Time          `json:\"rollback_time,omitempty\"`\n\tResult           string              `json:\"result,omitempty\"`\n\tRollbackReason   string              `json:\"rollback_reason,omitempty\"`\n\tOptions          string              `json:\"options,omitempty\"`\n\tCustomData       string              `json:\"custom_data,omitempty\"`\n\tNextCronInterval int64               `json:\"next_cron_interval,omitempty\"`\n\tNextCronTime     *time.Time          `json:\"next_cron_time,omitempty\"`\n\tOwner            string              `json:\"owner,omitempty\"`\n\tExt              TransGlobalExt      `json:\"-\" gorm:\"-\"`\n\tExtData          string              `json:\"ext_data,omitempty\"` // storage of ext. a db field to store many values. like Options\n\tdtmcli.TransOptions\n}\n\n// TableName TableName\nfunc (g *TransGlobalStore) TableName() string {\n\treturn \"trans_global\"\n}\n\nfunc (g *TransGlobalStore) String() string {\n\treturn dtmimp.MustMarshalString(g)\n}\n\n// IsFinished return true if status == \"failed\" || status == \"succeed\"\nfunc (g *TransGlobalStore) IsFinished() bool {\n\treturn g.Status == dtmcli.StatusFailed || g.Status == dtmcli.StatusSucceed\n}\n\n// TransBranchStore branch transaction\ntype TransBranchStore struct {\n\tdtmutil.ModelBase\n\tGid          string     `json:\"gid,omitempty\"`\n\tURL          string     `json:\"url,omitempty\"`\n\tBinData      []byte     `json:\"bin_data,omitempty\"`\n\tBranchID     string     `json:\"branch_id,omitempty\"`\n\tOp           string     `json:\"op,omitempty\"`\n\tStatus       string     `json:\"status,omitempty\"`\n\tFinishTime   *time.Time `json:\"finish_time,omitempty\"`\n\tRollbackTime *time.Time `json:\"rollback_time,omitempty\"`\n\tError        error      `json:\"-\" gorm:\"-\"`\n}\n\n// TableName TableName\nfunc (b *TransBranchStore) TableName() string {\n\treturn \"trans_branch_op\"\n}\n\nfunc (b *TransBranchStore) String() string {\n\treturn dtmimp.MustMarshalString(*b)\n}\n\n// KVStore  defines Key-Value storage info\ntype KVStore struct {\n\tdtmutil.ModelBase\n\tCat     string `json:\"cat\"`\n\tK       string `json:\"k\"`\n\tV       string `json:\"v\"`\n\tVersion uint64 `json:\"version\"`\n}\n\n// TableName TableName\nfunc (k *KVStore) TableName() string {\n\treturn \"kv\"\n}\n"
  },
  {
    "path": "dtmsvr/svr.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmsvr\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc\"\n\t\"github.com/gin-gonic/gin\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgpb\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtmdriver\"\n\t\"github.com/dtm-labs/logger\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/reflection\"\n\t\"google.golang.org/grpc/status\"\n)\n\n// StartSvr StartSvr\nfunc StartSvr() *gin.Engine {\n\tlogger.Infof(\"start dtmsvr\")\n\tsetServerInfoMetrics()\n\n\tdtmcli.GetRestyClient().SetTimeout(time.Duration(conf.RequestTimeout) * time.Second)\n\tdtmgrpc.AddUnaryInterceptor(func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {\n\t\ttimeout := conf.RequestTimeout\n\t\tif v := dtmgimp.RequestTimeoutFromContext(ctx); v != 0 {\n\t\t\ttimeout = v\n\t\t}\n\t\tctx2, cancel := context.WithTimeout(ctx, time.Duration(timeout)*time.Second)\n\t\tdefer cancel()\n\t\treturn invoker(ctx2, method, req, reply, cc, opts...)\n\t})\n\n\t// start gin server\n\tapp := dtmutil.GetGinApp()\n\tapp = httpMetrics(app)\n\taddRoute(app)\n\taddJrpcRouter(app)\n\tlogger.Infof(\"dtmsvr http listen at: %d\", conf.HTTPPort)\n\tgo func() {\n\t\terr := app.Run(fmt.Sprintf(\":%d\", conf.HTTPPort))\n\t\tif err != nil {\n\t\t\tlogger.Errorf(\"start server err: %v\", err)\n\t\t}\n\t}()\n\n\t// start grpc server\n\tlis, err := net.Listen(\"tcp\", fmt.Sprintf(\":%d\", conf.GrpcPort))\n\tlogger.FatalIfError(err)\n\ts := grpc.NewServer(grpc.ChainUnaryInterceptor(grpcRecover, grpcMetrics, dtmgimp.GrpcServerLog))\n\tdtmgpb.RegisterDtmServer(s, &dtmServer{})\n\treflection.Register(s)\n\tlogger.Infof(\"grpc listening at %v\", lis.Addr())\n\tgo func() {\n\t\terr := s.Serve(lis)\n\t\tlogger.FatalIfError(err)\n\t}()\n\n\tfor i := 0; i < int(conf.UpdateBranchAsyncGoroutineNum); i++ {\n\t\tgo updateBranchAsync()\n\t}\n\tupdateTopicsMap()\n\tgo CronUpdateTopicsMap()\n\n\ttime.Sleep(100 * time.Millisecond)\n\terr = dtmdriver.Use(conf.MicroService.Driver)\n\tlogger.FatalIfError(err)\n\tlogger.Infof(\"RegisterService: %s\", conf.MicroService.Driver)\n\terr = dtmdriver.GetDriver().RegisterService(conf.MicroService.Target, conf.MicroService.EndPoint)\n\tlogger.FatalIfError(err)\n\treturn app\n}\n\n// PopulateDB setup mysql data\nfunc PopulateDB(skipDrop bool) {\n\tGetStore().PopulateData(skipDrop)\n}\n\n// UpdateBranchAsyncInterval interval to flush branch\nvar UpdateBranchAsyncInterval = 200 * time.Millisecond\nvar updateBranchAsyncChan = make(chan branchStatus, 1000)\n\nfunc updateBranchAsync() {\n\tflushBranchs := func() {\n\t\tdefer dtmutil.RecoverPanic(nil)\n\t\tupdates := []TransBranch{}\n\t\texists := map[string]bool{}\n\t\tstarted := time.Now()\n\t\tcheckInterval := 20 * time.Millisecond\n\t\tfor time.Since(started) < UpdateBranchAsyncInterval-checkInterval && len(updates) < 20 {\n\t\t\tselect {\n\t\t\tcase updateBranch := <-updateBranchAsyncChan:\n\t\t\t\tk := updateBranch.gid + updateBranch.branchID + \"-\" + updateBranch.op\n\t\t\t\tif !exists[k] { // postgres does not allow\n\t\t\t\t\texists[k] = true\n\t\t\t\t\tupdates = append(updates, TransBranch{\n\t\t\t\t\t\tGid:        updateBranch.gid,\n\t\t\t\t\t\tBranchID:   updateBranch.branchID,\n\t\t\t\t\t\tOp:         updateBranch.op,\n\t\t\t\t\t\tStatus:     updateBranch.status,\n\t\t\t\t\t\tFinishTime: updateBranch.finishTime,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\tcase <-time.After(checkInterval):\n\t\t\t}\n\t\t}\n\t\tfor i := 0; i < 3 && len(updates) > 0; i++ {\n\t\t\trowAffected, err := GetStore().UpdateBranches(updates, []string{\"status\", \"finish_time\", \"update_time\"})\n\n\t\t\tif err != nil {\n\t\t\t\tlogger.Errorf(\"async update branch status error: %v\", err)\n\t\t\t\ttime.Sleep(1 * time.Second)\n\t\t\t} else {\n\t\t\t\tlogger.Infof(\"flushed %d branch status to db. affected: %d\", len(updates), rowAffected)\n\t\t\t\tupdates = []TransBranch{}\n\t\t\t}\n\t\t}\n\n\t}\n\tfor { // flush branches every 200ms\n\t\tflushBranchs()\n\t}\n}\n\nfunc grpcRecover(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (res interface{}, rerr error) {\n\tdefer func() {\n\t\tif x := recover(); x != nil {\n\t\t\trerr = status.Errorf(codes.Internal, \"%v\", x)\n\t\t\tlogger.Errorf(\"dtm server panic: %v\", x)\n\t\t}\n\t}()\n\tres, rerr = handler(ctx, req)\n\treturn\n}\n"
  },
  {
    "path": "dtmsvr/topics.go",
    "content": "package dtmsvr\n\nimport (\n\t\"errors\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/logger\"\n)\n\nconst (\n\ttopicsCat = \"topics\"\n)\n\nvar topicsMap = map[string]Topic{}\n\n// Topic define topic info\ntype Topic struct {\n\tName        string       `json:\"k\"`\n\tSubscribers []Subscriber `json:\"v\"`\n\tVersion     uint64       `json:\"version\"`\n}\n\n// Subscriber define subscriber info\ntype Subscriber struct {\n\tURL    string `json:\"url\"`\n\tRemark string `json:\"remark\"`\n}\n\nfunc topic2urls(topic string) []string {\n\turls := make([]string, len(topicsMap[topic].Subscribers))\n\tfor k, subscriber := range topicsMap[topic].Subscribers {\n\t\turls[k] = subscriber.URL\n\t}\n\treturn urls\n}\n\n// Subscribe subscribes topic, create topic if not exist\nfunc Subscribe(topic, url, remark string) error {\n\tif topic == \"\" {\n\t\treturn errors.New(\"empty topic\")\n\t}\n\tif url == \"\" {\n\t\treturn errors.New(\"empty url\")\n\t}\n\n\tnewSubscriber := Subscriber{\n\t\tURL:    url,\n\t\tRemark: remark,\n\t}\n\tkvs := GetStore().FindKV(topicsCat, topic)\n\tif len(kvs) == 0 {\n\t\treturn GetStore().CreateKV(topicsCat, topic, dtmimp.MustMarshalString([]Subscriber{newSubscriber}))\n\t}\n\n\tsubscribers := []Subscriber{}\n\tdtmimp.MustUnmarshalString(kvs[0].V, &subscribers)\n\tfor _, subscriber := range subscribers {\n\t\tif subscriber.URL == url {\n\t\t\treturn errors.New(\"this url exists\")\n\t\t}\n\t}\n\tsubscribers = append(subscribers, newSubscriber)\n\tkvs[0].V = dtmimp.MustMarshalString(subscribers)\n\treturn GetStore().UpdateKV(&kvs[0])\n}\n\n// Unsubscribe unsubscribes the topic\nfunc Unsubscribe(topic, url string) error {\n\tif topic == \"\" {\n\t\treturn errors.New(\"empty topic\")\n\t}\n\tif url == \"\" {\n\t\treturn errors.New(\"empty url\")\n\t}\n\n\tkvs := GetStore().FindKV(topicsCat, topic)\n\tif len(kvs) == 0 {\n\t\treturn errors.New(\"no such a topic\")\n\t}\n\tsubscribers := []Subscriber{}\n\tdtmimp.MustUnmarshalString(kvs[0].V, &subscribers)\n\tif len(subscribers) == 0 {\n\t\treturn errors.New(\"this topic is empty\")\n\t}\n\tn := len(subscribers)\n\tfor k, subscriber := range subscribers {\n\t\tif subscriber.URL == url {\n\t\t\tsubscribers = append(subscribers[:k], subscribers[k+1:]...)\n\t\t\tbreak\n\t\t}\n\t}\n\tif len(subscribers) == n {\n\t\treturn errors.New(\"no such an url \")\n\t}\n\tkvs[0].V = dtmimp.MustMarshalString(subscribers)\n\treturn GetStore().UpdateKV(&kvs[0])\n}\n\n// updateTopicsMap updates the topicsMap variable, unsafe for concurrent\nfunc updateTopicsMap() {\n\tkvs := GetStore().FindKV(topicsCat, \"\")\n\tfor _, kv := range kvs {\n\t\ttopic := topicsMap[kv.K]\n\t\tif topic.Version >= kv.Version {\n\t\t\tcontinue\n\t\t}\n\t\tnewTopic := Topic{}\n\t\tnewTopic.Name = kv.K\n\t\tnewTopic.Version = kv.Version\n\t\tdtmimp.MustUnmarshalString(kv.V, &newTopic.Subscribers)\n\t\ttopicsMap[kv.K] = newTopic\n\t\tlogger.Infof(\"topic updated. old topic:%v new topic:%v\", topicsMap[kv.K], newTopic)\n\t}\n\tlogger.Debugf(\"all topic updated. topic:%v\", topicsMap)\n}\n"
  },
  {
    "path": "dtmsvr/trans_class.go",
    "content": "package dtmsvr\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgpb\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// TransGlobal global transaction\ntype TransGlobal struct {\n\tstorage.TransGlobalStore\n\tReqExtra         map[string]string `json:\"req_extra\"`\n\tContext          context.Context\n\tlastTouched      time.Time // record the start time of process\n\tupdateBranchSync bool\n}\n\nfunc (t *TransGlobal) setupPayloads() {\n\t// Payloads will be store in BinPayloads, Payloads is only used to Unmarshal\n\tfor _, p := range t.Payloads {\n\t\tt.BinPayloads = append(t.BinPayloads, []byte(p))\n\t}\n\tfor _, d := range t.Steps {\n\t\tif d[\"data\"] != \"\" {\n\t\t\tt.BinPayloads = append(t.BinPayloads, []byte(d[\"data\"]))\n\t\t}\n\t}\n\tif t.Protocol == \"\" {\n\t\tt.Protocol = dtmimp.ProtocolHTTP\n\t}\n\n}\n\n// TransBranch branch transaction\ntype TransBranch = storage.TransBranchStore\n\ntype transProcessor interface {\n\tGenBranches() []TransBranch\n\tProcessOnce(ctx context.Context, branches []TransBranch) error\n}\n\ntype processorCreator func(*TransGlobal) transProcessor\n\nvar processorFac = map[string]processorCreator{}\n\nfunc registorProcessorCreator(transType string, creator processorCreator) {\n\tprocessorFac[transType] = creator\n}\n\nfunc (t *TransGlobal) getProcessor() transProcessor {\n\treturn processorFac[t.TransType](t)\n}\n\ntype cronType int\n\nconst (\n\tcronBackoff cronType = iota\n\tcronReset\n\tcronKeep\n)\n\n// TransFromContext TransFromContext\nfunc TransFromContext(c *gin.Context) *TransGlobal {\n\tb, err := c.GetRawData()\n\te2p(err)\n\tm := TransGlobal{}\n\tdtmimp.MustUnmarshal(b, &m)\n\tm.Status = dtmimp.Escape(m.Status)\n\tm.Gid = dtmimp.Escape(m.Gid)\n\tlogger.Debugf(\"creating trans in prepare\")\n\tm.setupPayloads()\n\tm.Ext.Headers = map[string]string{}\n\treturn &m\n}\n\n// TransFromDtmRequest TransFromContext\nfunc TransFromDtmRequest(ctx context.Context, c *dtmgpb.DtmRequest) *TransGlobal {\n\to := &dtmgpb.DtmTransOptions{}\n\tif c.TransOptions != nil {\n\t\to = c.TransOptions\n\t}\n\tr := TransGlobal{TransGlobalStore: storage.TransGlobalStore{\n\t\tGid:            c.Gid,\n\t\tTransType:      c.TransType,\n\t\tQueryPrepared:  c.QueryPrepared,\n\t\tProtocol:       \"grpc\",\n\t\tBinPayloads:    c.BinPayloads,\n\t\tCustomData:     c.CustomedData,\n\t\tRollbackReason: c.RollbackReason,\n\t\tTransOptions: dtmcli.TransOptions{\n\t\t\tWaitResult:     o.WaitResult,\n\t\t\tTimeoutToFail:  o.TimeoutToFail,\n\t\t\tRetryInterval:  o.RetryInterval,\n\t\t\tBranchHeaders:  o.BranchHeaders,\n\t\t\tRequestTimeout: o.RequestTimeout,\n\t\t\tRetryLimit:     o.RetryLimit,\n\t\t},\n\t}}\n\tr.ReqExtra = c.ReqExtra\n\tr.Context = ctx\n\tif c.Steps != \"\" {\n\t\tdtmimp.MustUnmarshalString(c.Steps, &r.Steps)\n\t}\n\treturn &r\n}\n"
  },
  {
    "path": "dtmsvr/trans_process.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmsvr\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/logger\"\n)\n\n// Process process global transaction once\nfunc (t *TransGlobal) Process(branches []TransBranch) error {\n\tr := t.process(branches)\n\ttransactionMetrics(t, r == nil)\n\treturn r\n}\n\nfunc (t *TransGlobal) process(branches []TransBranch) error {\n\tif t.Options != \"\" {\n\t\tdtmimp.MustUnmarshalString(t.Options, &t.TransOptions)\n\t}\n\tif t.ExtData != \"\" {\n\t\tdtmimp.MustUnmarshalString(t.ExtData, &t.Ext)\n\t}\n\tif !t.WaitResult {\n\t\tctx := NewAsyncContext(t.Context)\n\t\tgo func(ctx context.Context) {\n\t\t\terr := t.processInner(ctx, branches)\n\t\t\tif err != nil && !errors.Is(err, dtmimp.ErrOngoing) {\n\t\t\t\tlogger.Errorf(\"processInner err: %v\", err)\n\t\t\t}\n\t\t}(ctx)\n\t\treturn nil\n\t}\n\tsubmitting := t.Status == dtmcli.StatusSubmitted\n\terr := t.processInner(t.Context, branches)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif submitting && t.Status != dtmcli.StatusSucceed {\n\t\tif t.RollbackReason != \"\" {\n\t\t\treturn dtmcli.ErrorMessage2Error(t.RollbackReason, dtmcli.ErrFailure)\n\t\t}\n\t\treturn fmt.Errorf(\"wait result not return success: %w\", dtmcli.ErrFailure)\n\t}\n\treturn nil\n}\n\nfunc (t *TransGlobal) processInner(ctx context.Context, branches []TransBranch) (rerr error) {\n\tdefer handlePanic(&rerr)\n\tdefer func() {\n\t\tif rerr != nil && !errors.Is(rerr, dtmcli.ErrOngoing) {\n\t\t\tlogger.Errorf(\"processInner got error: %s\", rerr.Error())\n\t\t}\n\t\tif TransProcessedTestChan != nil {\n\t\t\tlogger.Debugf(\"processed: %s\", t.Gid)\n\t\t\tTransProcessedTestChan <- t.Gid\n\t\t\tlogger.Debugf(\"notified: %s\", t.Gid)\n\t\t}\n\t}()\n\tlogger.Debugf(\"processing: %s status: %s\", t.Gid, t.Status)\n\tt.lastTouched = time.Now()\n\trerr = t.getProcessor().ProcessOnce(ctx, branches)\n\treturn\n}\n\nfunc (t *TransGlobal) saveNew() ([]TransBranch, error) {\n\tt.NextCronInterval = t.getNextCronInterval(cronReset)\n\tt.NextCronTime = dtmutil.GetNextTime(t.NextCronInterval)\n\tt.ExtData = dtmimp.MustMarshalString(t.Ext)\n\tif t.ExtData == \"{}\" {\n\t\tt.ExtData = \"\"\n\t}\n\tt.Options = dtmimp.MustMarshalString(t.TransOptions)\n\tif t.Options == \"{}\" {\n\t\tt.Options = \"\"\n\t}\n\tnow := time.Now()\n\tt.CreateTime = &now\n\tt.UpdateTime = &now\n\tbranches := t.getProcessor().GenBranches()\n\tfor i := range branches {\n\t\tbranches[i].CreateTime = &now\n\t\tbranches[i].UpdateTime = &now\n\t}\n\terr := GetStore().MaySaveNewTrans(&t.TransGlobalStore, branches)\n\tlogger.Infof(\"MaySaveNewTrans result: %v, global: %v branches: %v\",\n\t\terr, t.TransGlobalStore.String(), dtmimp.MustMarshalString(branches))\n\treturn branches, err\n}\n"
  },
  {
    "path": "dtmsvr/trans_status.go",
    "content": "package dtmsvr\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtmdriver\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/lithammer/shortuuid/v3\"\n\t\"google.golang.org/grpc/metadata\"\n)\n\n// touchCronTime Based on ctype or delay set nextCronTime\n// delay = 0 ,use ctype set nextCronTime and nextCronInterval\n// delay > 0 ,use delay set nextCronTime ，use ctype set nextCronInterval\nfunc (t *TransGlobal) touchCronTime(ctype cronType, delay uint64) {\n\tt.lastTouched = time.Now()\n\tnextCronInterval := t.getNextCronInterval(ctype)\n\n\tvar nextCronTime *time.Time\n\tif delay > 0 {\n\t\tnextCronTime = dtmutil.GetNextTime(int64(delay))\n\t} else {\n\t\tnextCronTime = dtmutil.GetNextTime(nextCronInterval)\n\t}\n\n\tGetStore().TouchCronTime(&t.TransGlobalStore, nextCronInterval, nextCronTime)\n\tlogger.Infof(\"TouchCronTime for: %s\", t.TransGlobalStore.String())\n}\n\ntype changeStatusParams struct {\n\trollbackReason string\n\tresult         string\n}\n\ntype changeStatusOption func(c *changeStatusParams)\n\nfunc withRollbackReason(rollbackReason string) changeStatusOption {\n\treturn func(c *changeStatusParams) {\n\t\tc.rollbackReason = rollbackReason\n\t}\n}\n\nfunc withResult(result string) changeStatusOption {\n\treturn func(c *changeStatusParams) {\n\t\tc.result = result\n\t}\n}\n\nfunc (t *TransGlobal) changeStatus(status string, opts ...changeStatusOption) {\n\tstatusParams := &changeStatusParams{}\n\tfor _, opt := range opts {\n\t\topt(statusParams)\n\t}\n\tupdates := []string{\"status\", \"update_time\"}\n\tnow := time.Now()\n\tif status == dtmcli.StatusSucceed {\n\t\tt.FinishTime = &now\n\t\tupdates = append(updates, \"finish_time\")\n\t} else if status == dtmcli.StatusFailed {\n\t\tt.RollbackTime = &now\n\t\tupdates = append(updates, \"rollback_time\")\n\t}\n\tif statusParams.rollbackReason != \"\" {\n\t\tt.RollbackReason = statusParams.rollbackReason\n\t\tupdates = append(updates, \"rollback_reason\")\n\t}\n\tif statusParams.result != \"\" {\n\t\tt.Result = statusParams.result\n\t\tupdates = append(updates, \"result\")\n\t}\n\tt.UpdateTime = &now\n\tGetStore().ChangeGlobalStatus(&t.TransGlobalStore, status, updates, status == dtmcli.StatusSucceed || status == dtmcli.StatusFailed)\n\tlogger.Infof(\"ChangeGlobalStatus to %s ok for %s\", status, t.TransGlobalStore.String())\n\tt.Status = status\n}\n\nfunc (t *TransGlobal) resetNextCronTime() error {\n\terr := GetStore().ResetTransGlobalCronTime(&t.TransGlobalStore)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlogger.Infof(\"ResetTransGlobalCronTime to now ok for %s\", t.TransGlobalStore.String())\n\treturn nil\n}\n\nfunc (t *TransGlobal) changeBranchStatus(b *TransBranch, status string, branchPos int) {\n\tnow := time.Now()\n\tb.Status = status\n\tb.FinishTime = &now\n\tb.UpdateTime = &now\n\tif conf.Store.Driver != dtmimp.DBTypeMysql && conf.Store.Driver != dtmimp.DBTypePostgres || conf.UpdateBranchSync > 0 || t.updateBranchSync {\n\t\tGetStore().LockGlobalSaveBranches(t.Gid, t.Status, []TransBranch{*b}, branchPos)\n\t\tlogger.Infof(\"LockGlobalSaveBranches ok: gid: %s old status: %s branches: %s\",\n\t\t\tb.Gid, dtmcli.StatusPrepared, b.String())\n\t} else { // for better performance, batch the updates of branch status\n\t\tupdateBranchAsyncChan <- branchStatus{gid: t.Gid, branchID: b.BranchID, op: b.Op, status: status, finishTime: &now}\n\t}\n}\n\nfunc (t *TransGlobal) isTimeout() bool {\n\ttimeout := t.TimeoutToFail\n\tif t.TimeoutToFail == 0 && t.TransType != \"saga\" {\n\t\ttimeout = conf.TimeoutToFail\n\t}\n\tif timeout == 0 {\n\t\treturn false\n\t}\n\treturn time.Since(*t.CreateTime)+NowForwardDuration >= time.Duration(timeout)*time.Second\n}\n\nfunc (t *TransGlobal) needDelay(delay uint64) bool {\n\treturn time.Since(*t.CreateTime)+CronForwardDuration < time.Duration(delay)*time.Second\n}\n\nfunc (t *TransGlobal) needProcess() bool {\n\treturn t.Status == dtmcli.StatusSubmitted || t.Status == dtmcli.StatusAborting || t.Status == dtmcli.StatusPrepared && t.isTimeout()\n}\n\nfunc (t *TransGlobal) getURLResult(ctx context.Context, uri string, branchID, op string, branchPayload []byte) error {\n\tif uri == \"\" { // empty url is success\n\t\treturn nil\n\t}\n\tif t.Protocol == dtmimp.ProtocolHTTP || strings.HasPrefix(uri, \"http://\") || strings.HasPrefix(uri, \"https://\") {\n\t\tif t.Protocol == \"json-rpc\" && strings.Contains(uri, \"method\") {\n\t\t\treturn t.getJSONRPCResult(uri, branchID, op, branchPayload)\n\t\t}\n\t\treturn t.getHTTPResult(uri, branchID, op, branchPayload)\n\t}\n\treturn t.getGrpcResult(ctx, uri, branchID, op, branchPayload)\n}\n\nfunc (t *TransGlobal) getHTTPResult(uri string, branchID, op string, branchPayload []byte) error {\n\trc := dtmimp.GetRestyClient2(time.Duration(t.RequestTimeout) * time.Second)\n\tresp, err := rc.R().SetBody(string(branchPayload)).\n\t\tSetQueryParams(map[string]string{\n\t\t\t\"gid\":        t.Gid,\n\t\t\t\"trans_type\": t.TransType,\n\t\t\t\"branch_id\":  branchID,\n\t\t\t\"op\":         op,\n\t\t}).\n\t\tSetHeader(\"Content-type\", \"application/json\").\n\t\tSetHeaders(t.Ext.Headers).\n\t\tSetHeaders(t.TransOptions.BranchHeaders).\n\t\tExecute(t.determineHTTPRequestMethod(branchPayload), uri)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn dtmcli.HTTPResp2DtmError(resp)\n}\n\nfunc (t *TransGlobal) determineHTTPRequestMethod(branchPayload []byte) string {\n\treturn dtmimp.If(len(branchPayload) != 0 || t.TransType == \"xa\", \"POST\", \"GET\").(string)\n}\n\nfunc (t *TransGlobal) getJSONRPCResult(uri string, branchID, op string, branchPayload []byte) error {\n\tvar params map[string]interface{}\n\tdtmimp.MustUnmarshal(branchPayload, &params)\n\tu, err := url.Parse(uri)\n\tdtmimp.E2P(err)\n\tparams[\"gid\"] = t.Gid\n\tparams[\"trans_type\"] = t.TransType\n\tparams[\"branch_id\"] = branchID\n\tparams[\"op\"] = op\n\trc := dtmimp.GetRestyClient2(time.Duration(t.RequestTimeout) * time.Second)\n\tresp, err := rc.R().SetBody(map[string]interface{}{\n\t\t\"params\":  params,\n\t\t\"jsonrpc\": \"2.0\",\n\t\t\"method\":  u.Query().Get(\"method\"),\n\t\t\"id\":      shortuuid.New(),\n\t}).\n\t\tSetHeader(\"Content-type\", \"application/json\").\n\t\tSetHeaders(t.Ext.Headers).\n\t\tSetHeaders(t.TransOptions.BranchHeaders).\n\t\tPost(uri)\n\tif err == nil {\n\t\terr = dtmcli.HTTPResp2DtmError(resp)\n\t}\n\tif err == nil {\n\t\terr = dtmimp.RespAsErrorByJSONRPC(resp)\n\t}\n\treturn err\n}\n\nfunc (t *TransGlobal) getGrpcResult(ctx context.Context, uri string, branchID, op string, branchPayload []byte) error {\n\t// grpc handler\n\tserver, method, err := dtmdriver.GetDriver().ParseServerMethod(uri)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tconn := dtmgimp.MustGetGrpcConn(server, true)\n\tctx = dtmgimp.TransInfo2Ctx(ctx, t.Gid, t.TransType, branchID, op, \"\")\n\tkvs := dtmgimp.Map2Kvs(t.Ext.Headers)\n\tkvs = append(kvs, dtmgimp.Map2Kvs(t.BranchHeaders)...)\n\tctx = metadata.AppendToOutgoingContext(ctx, kvs...)\n\tctx = dtmgimp.RequestTimeoutNewContext(ctx, t.RequestTimeout)\n\terr = conn.Invoke(ctx, method, branchPayload, &[]byte{})\n\tif err == nil {\n\t\treturn nil\n\t}\n\treturn dtmgrpc.GrpcError2DtmError(err)\n}\n\nfunc (t *TransGlobal) getBranchResult(ctx context.Context, branch *TransBranch) (string, error) {\n\terr := t.getURLResult(ctx, branch.URL, branch.BranchID, branch.Op, branch.BinData)\n\tif err == nil {\n\t\treturn dtmcli.StatusSucceed, nil\n\t} else if t.TransType == \"saga\" && branch.Op == dtmimp.OpAction && errors.Is(err, dtmcli.ErrFailure) {\n\t\tbranch.Error = fmt.Errorf(\"url:%s return failed: %w\", branch.URL, err)\n\t\treturn dtmcli.StatusFailed, nil\n\t} else if errors.Is(err, dtmcli.ErrOngoing) {\n\t\treturn \"\", dtmcli.ErrOngoing\n\t}\n\treturn \"\", fmt.Errorf(\"your http/grpc result should be specified as in:\\nhttp://d.dtm.pub/practice/arch.html#proto\\nunkown result will be retried: %w\", err)\n}\n\nfunc (t *TransGlobal) execBranch(ctx context.Context, branch *TransBranch, branchPos int) error {\n\tstatus, err := t.getBranchResult(ctx, branch)\n\tif status != \"\" {\n\t\tt.changeBranchStatus(branch, status, branchPos)\n\t}\n\tbranchMetrics(t, branch, status == dtmcli.StatusSucceed)\n\t// if time pass 1500ms and NextCronInterval is not default, then reset NextCronInterval\n\tif err == nil && (time.Since(t.lastTouched)+NowForwardDuration >= 1500*time.Millisecond ||\n\t\tt.NextCronInterval > conf.RetryInterval && t.NextCronInterval > t.RetryInterval) {\n\t\tt.touchCronTime(cronReset, 0)\n\t} else if err == dtmimp.ErrOngoing {\n\t\tt.touchCronTime(cronKeep, 0)\n\t} else if err != nil {\n\t\tt.touchCronTime(cronBackoff, 0)\n\t\tv := t.NextCronInterval / t.getNextCronInterval(cronReset)\n\t\tretryCount := int64(math.Log2(float64(v)))\n\t\tlogger.Debugf(\"origin: %d v: %d retryCount: %d\", t.getNextCronInterval(cronReset), v, retryCount)\n\t\tif retryCount >= conf.AlertRetryLimit && conf.AlertWebHook != \"\" {\n\t\t\t_, err2 := dtmcli.GetRestyClient().R().SetBody(gin.H{\n\t\t\t\t\"gid\":         t.Gid,\n\t\t\t\t\"status\":      t.Status,\n\t\t\t\t\"branch\":      branch.URL,\n\t\t\t\t\"error\":       err.Error(),\n\t\t\t\t\"retry_count\": retryCount,\n\t\t\t}).Post(conf.AlertWebHook)\n\t\t\tif err2 != nil {\n\t\t\t\tlogger.Errorf(\"alerting webhook error: %v\", err2)\n\t\t\t}\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (t *TransGlobal) getNextCronInterval(ctype cronType) int64 {\n\tif ctype == cronBackoff {\n\t\treturn t.NextCronInterval * 2\n\t}\n\tif ctype == cronKeep {\n\t\treturn t.NextCronInterval\n\t}\n\tif t.RetryInterval != 0 {\n\t\treturn t.RetryInterval\n\t}\n\tif t.TimeoutToFail > 0 && t.TimeoutToFail < conf.RetryInterval {\n\t\treturn t.TimeoutToFail\n\t}\n\treturn conf.RetryInterval\n}\n"
  },
  {
    "path": "dtmsvr/trans_type_msg.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmsvr\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/logger\"\n)\n\ntype transMsgProcessor struct {\n\t*TransGlobal\n}\n\nfunc init() {\n\tregistorProcessorCreator(\"msg\", func(trans *TransGlobal) transProcessor { return &transMsgProcessor{TransGlobal: trans} })\n}\n\nfunc (t *transMsgProcessor) GenBranches() []TransBranch {\n\tbranches := []TransBranch{}\n\tfor i, step := range t.Steps {\n\t\tmayTopic := strings.TrimPrefix(step[dtmimp.OpAction], dtmimp.MsgTopicPrefix)\n\t\turls := dtmimp.If(mayTopic == step[dtmimp.OpAction], []string{mayTopic}, topic2urls(mayTopic)).([]string)\n\t\tif len(urls) == 0 {\n\t\t\te2p(errors.New(\"topic not found\"))\n\t\t}\n\t\tfor j, url := range urls {\n\t\t\tb := TransBranch{\n\t\t\t\tGid:      t.Gid,\n\t\t\t\tBranchID: fmt.Sprintf(\"%02d%s\", i+1, dtmimp.If(len(urls) == 1, \"\", fmt.Sprintf(\"-%02d\", j+1)).(string)),\n\t\t\t\tBinData:  t.BinPayloads[i],\n\t\t\t\tURL:      url,\n\t\t\t\tOp:       dtmimp.OpAction,\n\t\t\t\tStatus:   dtmcli.StatusPrepared,\n\t\t\t}\n\t\t\tbranches = append(branches, b)\n\t\t}\n\t}\n\treturn branches\n}\n\ntype cMsgCustom struct {\n\tDelay uint64 //delay call branch, unit second\n}\n\nfunc (t *TransGlobal) mayQueryPrepared(ctx context.Context) {\n\tif !t.needProcess() || t.Status == dtmcli.StatusSubmitted {\n\t\treturn\n\t}\n\terr := t.getURLResult(ctx, t.QueryPrepared, \"00\", \"msg\", nil)\n\tif err == nil {\n\t\tt.changeStatus(dtmcli.StatusSubmitted)\n\t} else if errors.Is(err, dtmcli.ErrFailure) {\n\t\tt.changeStatus(dtmcli.StatusFailed)\n\t} else if errors.Is(err, dtmcli.ErrOngoing) {\n\t\tt.touchCronTime(cronReset, 0)\n\t} else {\n\t\tlogger.Errorf(\"getting result failed for %s. error: %v\", t.QueryPrepared, err)\n\t\tt.touchCronTime(cronBackoff, 0)\n\t}\n}\n\nfunc (t *transMsgProcessor) ProcessOnce(ctx context.Context, branches []TransBranch) error {\n\tt.mayQueryPrepared(ctx)\n\tif !t.needProcess() || t.Status == dtmcli.StatusPrepared {\n\t\treturn nil\n\t}\n\tcmc := cMsgCustom{Delay: 0}\n\tif t.CustomData != \"\" {\n\t\tdtmimp.MustUnmarshalString(t.CustomData, &cmc)\n\t}\n\n\tif cmc.Delay > 0 && t.needDelay(cmc.Delay) {\n\t\tt.touchCronTime(cronKeep, cmc.Delay)\n\t\treturn nil\n\t}\n\tvar started int\n\tresultsChan := make(chan error, len(branches))\n\tvar err error\n\tfor i := range branches {\n\t\tb := &branches[i]\n\t\tif b.Op != dtmimp.OpAction || b.Status != dtmcli.StatusPrepared {\n\t\t\tcontinue\n\t\t}\n\t\tif t.Concurrent {\n\t\t\tcopyCtx := NewAsyncContext(ctx)\n\t\t\tstarted++\n\t\t\tgo func(ctx context.Context, pos int) {\n\t\t\t\tresultsChan <- t.execBranch(ctx, b, pos)\n\t\t\t}(copyCtx, i)\n\t\t} else {\n\t\t\terr = t.execBranch(ctx, b, i)\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tfor i := 0; i < started && err == nil; i++ {\n\t\terr = <-resultsChan\n\t}\n\tif err == dtmcli.ErrOngoing {\n\t\treturn nil\n\t} else if err != nil {\n\t\treturn err\n\t}\n\tt.changeStatus(dtmcli.StatusSucceed)\n\treturn nil\n}\n"
  },
  {
    "path": "dtmsvr/trans_type_saga.go",
    "content": "package dtmsvr\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/logger\"\n)\n\ntype transSagaProcessor struct {\n\t*TransGlobal\n}\n\nfunc init() {\n\tregistorProcessorCreator(\"saga\", func(trans *TransGlobal) transProcessor {\n\t\treturn &transSagaProcessor{TransGlobal: trans}\n\t})\n}\n\nfunc (t *transSagaProcessor) GenBranches() []TransBranch {\n\tbranches := []TransBranch{}\n\tfor i, step := range t.Steps {\n\t\tbranch := fmt.Sprintf(\"%02d\", i+1)\n\t\tfor _, op := range []string{dtmimp.OpCompensate, dtmimp.OpAction} {\n\t\t\tbranches = append(branches, TransBranch{\n\t\t\t\tGid:      t.Gid,\n\t\t\t\tBranchID: branch,\n\t\t\t\tBinData:  t.BinPayloads[i],\n\t\t\t\tURL:      step[op],\n\t\t\t\tOp:       op,\n\t\t\t\tStatus:   dtmcli.StatusPrepared,\n\t\t\t})\n\t\t}\n\t}\n\treturn branches\n}\n\ntype cSagaCustom struct {\n\tOrders     map[int][]int `json:\"orders\"`\n\tConcurrent bool          `json:\"concurrent\"`\n\tcOrders    map[int][]int\n}\n\ntype branchResult struct {\n\tindex   int\n\tstatus  string\n\tstarted bool\n\top      string\n\terr     error\n}\n\nfunc (t *transSagaProcessor) ProcessOnce(ctx context.Context, branches []TransBranch) error {\n\t// when saga tasks is fetched, it always need to process\n\tlogger.Debugf(\"status: %s timeout: %t\", t.Status, t.isTimeout())\n\tif t.Status == dtmcli.StatusSubmitted && t.isTimeout() {\n\t\tt.changeStatus(dtmcli.StatusAborting, withRollbackReason(fmt.Sprintf(\"Timeout after %d seconds\", t.TimeoutToFail)))\n\t}\n\tn := len(branches)\n\n\tcsc := cSagaCustom{Orders: map[int][]int{}, cOrders: map[int][]int{}}\n\tif t.CustomData != \"\" {\n\t\tdtmimp.MustUnmarshalString(t.CustomData, &csc)\n\t\tfor k, v := range csc.Orders {\n\t\t\tfor _, b := range v {\n\t\t\t\tcsc.cOrders[b] = append(csc.cOrders[b], k)\n\t\t\t}\n\t\t}\n\t}\n\tif csc.Concurrent || t.TimeoutToFail > 0 { // when saga is not normal, update branch sync\n\t\tt.updateBranchSync = true\n\t}\n\t// resultStats\n\tvar rsAToStart, rsAStarted, rsADone, rsAFailed, rsASucceed, rsCToStart, rsCDone, rsCSucceed int\n\tvar failureError error\n\tbranchResults := make([]branchResult, n) // save the branch result\n\tfor i := 0; i < n; i++ {\n\t\tb := branches[i]\n\t\tif b.Op == dtmimp.OpAction {\n\t\t\tif b.Status == dtmcli.StatusPrepared {\n\t\t\t\trsAToStart++\n\t\t\t} else if b.Status == dtmcli.StatusFailed {\n\t\t\t\trsAFailed++\n\t\t\t}\n\t\t}\n\t\tbranchResults[i] = branchResult{index: i, status: branches[i].Status, op: branches[i].Op}\n\t}\n\tshouldRun := func(current int) bool {\n\t\t// if !csc.Concurrent，then check the branch in previous step is succeed\n\t\tif !csc.Concurrent && current >= 2 && branchResults[current-2].status != dtmcli.StatusSucceed {\n\t\t\treturn false\n\t\t}\n\t\t// if csc.concurrent, then check the Orders. origin one step correspond to 2 step in dtmsvr\n\t\tfor _, pre := range csc.Orders[current/2] {\n\t\t\tif branchResults[pre*2+1].status != dtmcli.StatusSucceed {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\tshouldRollback := func(current int) bool {\n\t\trollbacked := func(i int) bool {\n\t\t\t// current compensate op rollbacked or related action still prepared\n\t\t\treturn branchResults[i].status == dtmcli.StatusSucceed || branchResults[i+1].status == dtmcli.StatusPrepared\n\t\t}\n\t\tif rollbacked(current) {\n\t\t\treturn false\n\t\t}\n\t\t// if !csc.Concurrent，then check the branch in next step is rollbacked\n\t\tif !csc.Concurrent && current < n-2 && !rollbacked(current+2) {\n\t\t\treturn false\n\t\t}\n\t\t// if csc.concurrent, then check the cOrders. origin one step correspond to 2 step in dtmsvr\n\t\tfor _, next := range csc.cOrders[current/2] {\n\t\t\tif !rollbacked(2 * next) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\tresultChan := make(chan branchResult, n)\n\tasyncExecBranch := func(ctx context.Context, i int) {\n\t\tvar err error\n\t\tdefer func() {\n\t\t\tif x := recover(); x != nil {\n\t\t\t\terr = dtmimp.AsError(x)\n\t\t\t}\n\t\t\tresultChan <- branchResult{index: i, status: branches[i].Status, op: branches[i].Op, err: branches[i].Error}\n\t\t\tif err != nil && !errors.Is(err, dtmcli.ErrOngoing) {\n\t\t\t\tlogger.Errorf(\"exec branch %s %s %s error: %v\", branches[i].BranchID, branches[i].Op, branches[i].URL, err)\n\t\t\t}\n\t\t}()\n\t\terr = t.execBranch(ctx, &branches[i], i)\n\t}\n\tpickToRunActions := func() []int {\n\t\ttoRun := []int{}\n\t\tfor current := 1; current < n; current += 2 {\n\t\t\tbr := &branchResults[current]\n\t\t\tif !br.started && br.status == dtmcli.StatusPrepared && shouldRun(current) {\n\t\t\t\ttoRun = append(toRun, current)\n\t\t\t}\n\t\t}\n\t\tlogger.Debugf(\"toRun picked for action is: %v branchResults: %v compensate orders: %v\", toRun, branchResults, csc.cOrders)\n\t\treturn toRun\n\t}\n\tpickToRunCompensates := func() []int {\n\t\ttoRun := []int{}\n\t\tfor current := n - 2; current >= 0; current -= 2 {\n\t\t\tbr := &branchResults[current]\n\t\t\tif !br.started && br.status == dtmcli.StatusPrepared && shouldRollback(current) {\n\t\t\t\ttoRun = append(toRun, current)\n\t\t\t}\n\t\t}\n\t\tlogger.Debugf(\"toRun picked for compensate is: %v branchResults: %v compensate orders: %v\", toRun, branchResults, csc.cOrders)\n\t\treturn toRun\n\t}\n\trunBranches := func(toRun []int) {\n\t\tfor _, b := range toRun {\n\t\t\tbranchResults[b].started = true\n\t\t\tif branchResults[b].op == dtmimp.OpAction {\n\t\t\t\trsAStarted++\n\t\t\t}\n\t\t\tcopyCtx := NewAsyncContext(ctx)\n\t\t\tgo asyncExecBranch(copyCtx, b)\n\t\t}\n\t}\n\twaitDoneOnce := func() {\n\t\tselect {\n\t\tcase r := <-resultChan:\n\t\t\tbr := &branchResults[r.index]\n\t\t\tbr.status = r.status\n\t\t\tif r.op == dtmimp.OpAction {\n\t\t\t\t// if t.RetryLimit > 0, should check the retry count\n\t\t\t\tif t.RetryLimit > 0 && (r.status == dtmcli.StatusPrepared || r.status == dtmcli.StatusSubmitted) {\n\t\t\t\t\t// if t.RetryCount < t.RetryLimit, branch will be retried util RetryLimit = 0\n\t\t\t\t\tif t.RetryCount < t.RetryLimit {\n\t\t\t\t\t\tt.RetryCount++\n\t\t\t\t\t\tlogger.Infof(\"Retrying branch %s %s %s, t.RetryLimit: %d, t.RetryCount: %d\",\n\t\t\t\t\t\t\tbranches[r.index].BranchID, branches[r.index].Op, branches[r.index].URL, t.RetryLimit, t.RetryCount)\n\t\t\t\t\t\tcopyCtx := NewAsyncContext(ctx)\n\t\t\t\t\t\tgo asyncExecBranch(copyCtx, r.index)\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\t// if t.RetryCount = t.RetryLimit, trans will be aborted\n\t\t\t\t\tt.changeStatus(dtmcli.StatusAborting, withRollbackReason(fmt.Sprintf(\"RetryCount is greater than RetryLimit, RetryLimit: %v\", t.RetryLimit)))\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\trsADone++\n\t\t\t\tif r.status == dtmcli.StatusFailed {\n\t\t\t\t\trsAFailed++\n\t\t\t\t\tfailureError = r.err\n\t\t\t\t} else if r.status == dtmcli.StatusSucceed {\n\t\t\t\t\trsASucceed++\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trsCDone++\n\t\t\t\tif r.status == dtmcli.StatusSucceed {\n\t\t\t\t\trsCSucceed++\n\t\t\t\t}\n\t\t\t}\n\t\t\tlogger.Debugf(\"branch done: %v\", r)\n\t\tcase <-time.After(time.Second * 3):\n\t\t\tlogger.Debugf(\"wait once for done\")\n\t\t}\n\t}\n\tprepareToCompensate := func() {\n\t\ttoRun := pickToRunActions()\n\t\tfor _, b := range toRun { // flag started\n\t\t\tbranchResults[b].started = true\n\t\t}\n\t\tfor i := 1; i < len(branchResults); i += 2 {\n\t\t\t// these branches may have run. so flag them to status succeed, then run the corresponding\n\t\t\t// compensate\n\t\t\tif branchResults[i].started && branchResults[i].status == dtmcli.StatusPrepared {\n\t\t\t\tbranchResults[i].status = dtmcli.StatusSucceed\n\t\t\t}\n\t\t}\n\t\tfor i, b := range branchResults {\n\t\t\tif b.op == dtmimp.OpCompensate && b.status != dtmcli.StatusSucceed &&\n\t\t\t\tbranchResults[i+1].status != dtmcli.StatusPrepared {\n\t\t\t\trsCToStart++\n\t\t\t}\n\t\t}\n\t\tlogger.Debugf(\"rsCToStart: %d branchResults: %v\", rsCToStart, branchResults)\n\t}\n\ttimeLimit := time.Now().Add(time.Duration(conf.RequestTimeout+2) * time.Second)\n\tfor time.Now().Before(timeLimit) && t.Status == dtmcli.StatusSubmitted && !t.isTimeout() && rsAFailed == 0 {\n\t\ttoRun := pickToRunActions()\n\t\trunBranches(toRun)\n\t\tif rsADone == rsAStarted { // no branch is running, so break\n\t\t\tbreak\n\t\t}\n\t\twaitDoneOnce()\n\t}\n\tif t.Status == dtmcli.StatusSubmitted && rsAFailed == 0 && rsAToStart == rsASucceed {\n\t\tt.changeStatus(dtmcli.StatusSucceed)\n\t\treturn nil\n\t}\n\tif t.Status == dtmcli.StatusSubmitted && rsAFailed > 0 {\n\t\tmsg := \"fail message lost\"\n\t\tif failureError != nil { // handle the case if branch failed and saved, and then crash\n\t\t\tmsg = failureError.Error()\n\t\t}\n\t\tt.changeStatus(dtmcli.StatusAborting, withRollbackReason(msg))\n\t}\n\tif t.Status == dtmcli.StatusSubmitted && t.isTimeout() {\n\t\tt.changeStatus(dtmcli.StatusAborting, withRollbackReason(fmt.Sprintf(\"Timeout after %d seconds\", t.TimeoutToFail)))\n\t}\n\tif t.Status == dtmcli.StatusAborting {\n\t\tprepareToCompensate()\n\t}\n\tfor time.Now().Before(timeLimit) && t.Status == dtmcli.StatusAborting {\n\t\ttoRun := pickToRunCompensates()\n\t\trunBranches(toRun)\n\t\tif rsCDone == rsCToStart { // no branch is running, so break\n\t\t\tbreak\n\t\t}\n\t\tlogger.Debugf(\"rsCDone: %d rsCToStart: %d\", rsCDone, rsCToStart)\n\t\twaitDoneOnce()\n\t}\n\tif t.Status == dtmcli.StatusAborting && rsCToStart == rsCSucceed {\n\t\tt.changeStatus(dtmcli.StatusFailed)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "dtmsvr/trans_type_tcc.go",
    "content": "package dtmsvr\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/logger\"\n)\n\ntype transTccProcessor struct {\n\t*TransGlobal\n}\n\nfunc init() {\n\tregistorProcessorCreator(\"tcc\", func(trans *TransGlobal) transProcessor { return &transTccProcessor{TransGlobal: trans} })\n}\n\nfunc (t *transTccProcessor) GenBranches() []TransBranch {\n\treturn []TransBranch{}\n}\n\nfunc (t *transTccProcessor) ProcessOnce(ctx context.Context, branches []TransBranch) error {\n\tif !t.needProcess() {\n\t\treturn nil\n\t}\n\tif t.Status == dtmcli.StatusPrepared && t.isTimeout() {\n\t\tt.changeStatus(dtmcli.StatusAborting, withRollbackReason(fmt.Sprintf(\"Timeout after %d seconds\", t.TimeoutToFail)))\n\t}\n\top := dtmimp.If(t.Status == dtmcli.StatusSubmitted, dtmimp.OpConfirm, dtmimp.OpCancel).(string)\n\tfor current := len(branches) - 1; current >= 0; current-- {\n\t\tif branches[current].Op == op && branches[current].Status == dtmcli.StatusPrepared {\n\t\t\tlogger.Debugf(\"branch info: current: %d ID: %d\", current, branches[current].ID)\n\t\t\terr := t.execBranch(ctx, &branches[current], current)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\tt.changeStatus(dtmimp.If(t.Status == dtmcli.StatusSubmitted, dtmcli.StatusSucceed, dtmcli.StatusFailed).(string))\n\treturn nil\n}\n"
  },
  {
    "path": "dtmsvr/trans_type_workflow.go",
    "content": "package dtmsvr\n\nimport (\n\t\"context\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\t\"github.com/dtm-labs/dtm/client/workflow/wfpb\"\n)\n\ntype transWorkflowProcessor struct {\n\t*TransGlobal\n}\n\nfunc init() {\n\tregistorProcessorCreator(\"workflow\", func(trans *TransGlobal) transProcessor { return &transWorkflowProcessor{TransGlobal: trans} })\n}\n\nfunc (t *transWorkflowProcessor) GenBranches() []TransBranch {\n\treturn []TransBranch{}\n}\n\ntype cWorkflowCustom struct {\n\tName string `json:\"name\"`\n\tData []byte `json:\"data\"`\n}\n\nfunc (t *transWorkflowProcessor) ProcessOnce(ctx context.Context, branches []TransBranch) error {\n\tif t.Status == dtmcli.StatusFailed || t.Status == dtmcli.StatusSucceed {\n\t\treturn nil\n\t}\n\n\tcmc := cWorkflowCustom{}\n\tdtmimp.MustUnmarshalString(t.CustomData, &cmc)\n\tdata := cmc.Data\n\tif t.Protocol == dtmimp.ProtocolGRPC {\n\t\twd := wfpb.WorkflowData{Data: cmc.Data}\n\t\tdata = dtmgimp.MustProtoMarshal(&wd)\n\t}\n\treturn t.getURLResult(ctx, t.QueryPrepared, \"00\", cmc.Name, data)\n}\n"
  },
  {
    "path": "dtmsvr/trans_type_xa.go",
    "content": "package dtmsvr\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n)\n\ntype transXaProcessor struct {\n\t*TransGlobal\n}\n\nfunc init() {\n\tregistorProcessorCreator(\"xa\", func(trans *TransGlobal) transProcessor { return &transXaProcessor{TransGlobal: trans} })\n}\n\nfunc (t *transXaProcessor) GenBranches() []TransBranch {\n\treturn []TransBranch{}\n}\n\nfunc (t *transXaProcessor) ProcessOnce(ctx context.Context, branches []TransBranch) error {\n\tif !t.needProcess() {\n\t\treturn nil\n\t}\n\tif t.Status == dtmcli.StatusPrepared && t.isTimeout() {\n\t\tt.changeStatus(dtmcli.StatusAborting, withRollbackReason(fmt.Sprintf(\"Timeout after %d seconds\", t.TimeoutToFail)))\n\t}\n\tcurrentType := dtmimp.If(t.Status == dtmcli.StatusSubmitted, dtmimp.OpCommit, dtmimp.OpRollback).(string)\n\tfor i, branch := range branches {\n\t\tif branch.Op == currentType && branch.Status != dtmcli.StatusSucceed {\n\t\t\terr := t.execBranch(ctx, &branch, i)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\tt.changeStatus(dtmimp.If(t.Status == dtmcli.StatusSubmitted, dtmcli.StatusSucceed, dtmcli.StatusFailed).(string))\n\treturn nil\n}\n"
  },
  {
    "path": "dtmsvr/utils.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmsvr\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/config\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage/registry\"\n\t\"github.com/lithammer/shortuuid/v3\"\n)\n\ntype branchStatus struct {\n\tgid        string\n\tbranchID   string\n\top         string\n\tstatus     string\n\tfinishTime *time.Time\n}\n\nvar e2p = dtmimp.E2P\n\nvar conf = &config.Config\n\n// GetStore returns storage.Store\nfunc GetStore() storage.Store {\n\treturn registry.GetStore()\n}\n\n// TransProcessedTestChan only for test usage. when transaction processed once, write gid to this chan\nvar TransProcessedTestChan chan string\n\n// GenGid generate gid, use uuid\nfunc GenGid() string {\n\treturn shortuuid.New()\n}\n\n// GetTransGlobal construct trans from db\nfunc GetTransGlobal(gid string) *TransGlobal {\n\ttrans := GetStore().FindTransGlobalStore(gid)\n\t//nolint:staticcheck\n\tdtmimp.PanicIf(trans == nil, fmt.Errorf(\"no TransGlobal with gid: %s found\", gid))\n\t//nolint:staticcheck\n\treturn &TransGlobal{TransGlobalStore: *trans}\n}\n\ntype asyncCtx struct {\n\tcontext.Context\n}\n\nfunc (a *asyncCtx) Deadline() (deadline time.Time, ok bool) {\n\treturn\n}\n\nfunc (a *asyncCtx) Done() <-chan struct{} {\n\treturn nil\n}\n\n// NewAsyncContext create a new async context\n// the context will not be canceled when the parent context is canceled\nfunc NewAsyncContext(ctx context.Context) context.Context {\n\tif ctx == nil {\n\t\treturn nil\n\t}\n\treturn &asyncCtx{Context: ctx}\n}\n"
  },
  {
    "path": "dtmsvr/utils_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmsvr\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"google.golang.org/grpc/metadata\"\n)\n\nfunc TestUtils(t *testing.T) {\n\tCronExpiredTrans(1)\n\tsleepCronTime()\n}\n\nfunc TestSetNextCron(t *testing.T) {\n\tconf.RetryInterval = 10\n\ttg := TransGlobal{}\n\ttg.NextCronInterval = conf.RetryInterval\n\ttg.RetryInterval = 15\n\tassert.Equal(t, int64(15), tg.getNextCronInterval(cronReset))\n\ttg.RetryInterval = 0\n\tassert.Equal(t, conf.RetryInterval, tg.getNextCronInterval(cronReset))\n\tassert.Equal(t, conf.RetryInterval*2, tg.getNextCronInterval(cronBackoff))\n\ttg.TimeoutToFail = 3\n\tassert.Equal(t, int64(3), tg.getNextCronInterval(cronReset))\n}\n\nfunc TestNewAsyncContext(t *testing.T) {\n\tvar key testContextType = \"key\"\n\tvar value testContextType = \"value\"\n\tctxWithValue := context.WithValue(context.Background(), key, value)\n\tnewCtx := NewAsyncContext(ctxWithValue)\n\tassert.Equal(t, ctxWithValue.Value(key), newCtx.Value(key))\n\n\tvar ctx context.Context\n\tnewCtx = NewAsyncContext(ctx)\n\tassert.Nil(t, newCtx)\n}\n\nfunc TestAsyncContext(t *testing.T) {\n\tctx := context.Background()\n\tcancelCtx2, cancel := context.WithCancel(ctx)\n\tasync := NewAsyncContext(cancelCtx2)\n\tcancelCtx3, cancel2 := context.WithCancel(async)\n\tdefer cancel2()\n\tcancel()\n\tselect {\n\tcase <-cancelCtx2.Done():\n\tdefault:\n\t\tassert.Failf(t, \"context should be canceled\", \"context should be canceled\")\n\t}\n\tselect {\n\tcase <-cancelCtx3.Done():\n\t\tassert.Failf(t, \"context should not be canceled\", \"context should not be canceled\")\n\tdefault:\n\t}\n}\n\ntype testContextType string\n\nfunc TestAsyncContextRecursive(t *testing.T) {\n\tvar key testContextType = \"key\"\n\tvar key2 testContextType = \"key2\"\n\tvar key3 testContextType = \"key3\"\n\tvar value testContextType = \"value\"\n\tvar value2 testContextType = \"value2\"\n\tvar value3 testContextType = \"value3\"\n\tvar nestedKey testContextType = \"nested_key\"\n\tvar nestedValue testContextType = \"nested_value\"\n\tctxWithValue := context.WithValue(context.Background(), key, value)\n\tnestedCtx := context.WithValue(ctxWithValue, nestedKey, nestedValue)\n\tcancelCtxx, cancel := context.WithCancel(nestedCtx)\n\tdefer cancel()\n\ttimerCtxx, cancel2 := context.WithTimeout(cancelCtxx, time.Duration(10)*time.Second)\n\tdefer cancel2()\n\ttimer2 := context.WithValue(timerCtxx, key2, value2)\n\ttimer3 := context.WithValue(timer2, key3, value3)\n\tnewCtx := NewAsyncContext(timer3)\n\n\tassert.Equal(t, timer3.Value(nestedKey), newCtx.Value(nestedKey))\n\tassert.Equal(t, timer3.Value(key), newCtx.Value(key))\n\tassert.Equal(t, timer3.Value(key2), newCtx.Value(key2))\n\tassert.Equal(t, timer3.Value(key3), newCtx.Value(key3))\n}\n\nfunc TestCopyContextWithMetadata(t *testing.T) {\n\tmd := metadata.New(map[string]string{\"key\": \"value\"})\n\tctx := metadata.NewIncomingContext(context.Background(), md)\n\tctx = metadata.NewOutgoingContext(ctx, md)\n\tnewCtx := NewAsyncContext(ctx)\n\n\tcopiedMD, ok := metadata.FromIncomingContext(newCtx)\n\tassert.True(t, ok)\n\tassert.Equal(t, 1, len(copiedMD[\"key\"]))\n\tassert.Equal(t, \"value\", copiedMD[\"key\"][0])\n\tcopiedMD, ok = metadata.FromOutgoingContext(newCtx)\n\tassert.True(t, ok)\n\tassert.Equal(t, 1, len(copiedMD[\"key\"]))\n\tassert.Equal(t, \"value\", copiedMD[\"key\"][0])\n}\n\nfunc BenchmarkCopyContext(b *testing.B) {\n\tvar key testContextType = \"key\"\n\tvar value testContextType = \"value\"\n\tctx := context.WithValue(context.Background(), key, value)\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tNewAsyncContext(ctx)\n\t}\n}\n"
  },
  {
    "path": "dtmutil/consts.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmutil\n\nconst (\n\t// DefaultHTTPServer default url for http server. used by test and examples\n\tDefaultHTTPServer = \"http://localhost:36789/api/dtmsvr\"\n\t// DefaultJrpcServer default url for http json-rpc server. used by test and examples\n\tDefaultJrpcServer = \"http://localhost:36789/api/json-rpc\"\n\t// DefaultGrpcServer default url for grpc server. used by test and examples\n\tDefaultGrpcServer = \"localhost:36790\"\n)\n"
  },
  {
    "path": "dtmutil/db.go",
    "content": "package dtmutil\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/logger\"\n\t_ \"github.com/go-sql-driver/mysql\" // register mysql driver\n\t_ \"github.com/lib/pq\"              // register postgres driver\n\n\t// _ \"github.com/microsoft/go-mssqldb\" // Microsoft's package conflicts with gorm's package: panic: sql: Register called twice for driver mssql\n\t\"gorm.io/driver/mysql\"\n\t\"gorm.io/driver/postgres\"\n\t\"gorm.io/driver/sqlserver\" // register sqlserver driver,\n\t\"gorm.io/gorm\"\n)\n\n// ModelBase model base for gorm to provide base fields\ntype ModelBase struct {\n\tID         uint64     `json:\"id\"`\n\tCreateTime *time.Time `json:\"create_time\" gorm:\"autoCreateTime\"`\n\tUpdateTime *time.Time `json:\"update_time\" gorm:\"autoUpdateTime\"`\n}\n\nfunc getGormDialetor(driver string, dsn string) gorm.Dialector {\n\tif driver == dtmcli.DBTypePostgres {\n\t\treturn postgres.Open(dsn)\n\t}\n\tif driver == dtmcli.DBTypeSQLServer {\n\t\treturn sqlserver.Open(dsn)\n\t}\n\tdtmimp.PanicIf(driver != dtmcli.DBTypeMysql, fmt.Errorf(\"unknown driver: %s\", driver))\n\treturn mysql.Open(dsn)\n}\n\nvar dbs sync.Map\n\n// DB provide more func over gorm.DB\ntype DB struct {\n\t*gorm.DB\n}\n\n// Must set must flag, panic when error occur\nfunc (m *DB) Must() *DB {\n\tdb := m.InstanceSet(\"ivy.must\", true)\n\treturn &DB{DB: db}\n}\n\n// ToSQLDB get the sql.DB\nfunc (m *DB) ToSQLDB() *sql.DB {\n\td, err := m.DB.DB()\n\tdtmimp.E2P(err)\n\treturn d\n}\n\ntype tracePlugin struct{}\n\nfunc (op *tracePlugin) Name() string {\n\treturn \"tracePlugin\"\n}\n\nfunc (op *tracePlugin) Initialize(db *gorm.DB) (err error) {\n\tbefore := func(db *gorm.DB) {\n\t\tdb.InstanceSet(\"ivy.startTime\", time.Now())\n\t}\n\n\tafter := func(db *gorm.DB) {\n\t\t_ts, _ := db.InstanceGet(\"ivy.startTime\")\n\t\tsql := db.Dialector.Explain(db.Statement.SQL.String(), db.Statement.Vars...)\n\t\tlogger.Debugf(\"used: %d ms affected: %d sql is: %s\", time.Since(_ts.(time.Time)).Milliseconds(), db.RowsAffected, sql)\n\t\tif v, ok := db.InstanceGet(\"ivy.must\"); ok && v.(bool) {\n\t\t\tif db.Error != nil && db.Error != gorm.ErrRecordNotFound {\n\t\t\t\tpanic(db.Error)\n\t\t\t}\n\t\t}\n\t}\n\n\tbeforeName := \"cb_before\"\n\tafterName := \"cb_after\"\n\n\tlogger.Debugf(\"installing db plugin: %s\", op.Name())\n\t// before\n\t_ = db.Callback().Create().Before(\"gorm:before_create\").Register(beforeName, before)\n\t_ = db.Callback().Query().Before(\"gorm:query\").Register(beforeName, before)\n\t_ = db.Callback().Delete().Before(\"gorm:before_delete\").Register(beforeName, before)\n\t_ = db.Callback().Update().Before(\"gorm:setup_reflect_value\").Register(beforeName, before)\n\t_ = db.Callback().Row().Before(\"gorm:row\").Register(beforeName, before)\n\t_ = db.Callback().Raw().Before(\"gorm:raw\").Register(beforeName, before)\n\n\t// after\n\t_ = db.Callback().Create().After(\"gorm:after_create\").Register(afterName, after)\n\t_ = db.Callback().Query().After(\"gorm:after_query\").Register(afterName, after)\n\t_ = db.Callback().Delete().After(\"gorm:after_delete\").Register(afterName, after)\n\t_ = db.Callback().Update().After(\"gorm:after_update\").Register(afterName, after)\n\t_ = db.Callback().Row().After(\"gorm:row\").Register(afterName, after)\n\t_ = db.Callback().Raw().After(\"gorm:raw\").Register(afterName, after)\n\treturn\n}\n\n// DbGet get db connection for specified conf\nfunc DbGet(conf dtmcli.DBConf, ops ...func(*gorm.DB)) *DB {\n\tdsn := dtmimp.GetDsn(conf)\n\tdb, ok := dbs.Load(dsn)\n\tif !ok {\n\t\tlogger.Infof(\"connecting '%s' '%s' '%s' '%d' '%s'\", conf.Driver, conf.Host, conf.User, conf.Port, conf.Db)\n\t\tdb1, err := gorm.Open(getGormDialetor(conf.Driver, dsn), &gorm.Config{\n\t\t\tSkipDefaultTransaction: true,\n\t\t})\n\t\tdtmimp.E2P(err)\n\t\terr = db1.Use(&tracePlugin{})\n\t\tdtmimp.E2P(err)\n\t\tdb = &DB{DB: db1}\n\t\tfor _, op := range ops {\n\t\t\top(db1)\n\t\t}\n\t\tdbs.Store(dsn, db)\n\t}\n\treturn db.(*DB)\n}\n"
  },
  {
    "path": "dtmutil/utils.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmutil\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/go-resty/resty/v2\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/logger\"\n)\n\n// GetGinApp init and return gin\nfunc GetGinApp() *gin.Engine {\n\tgin.SetMode(gin.ReleaseMode)\n\tapp := gin.New()\n\tapp.Use(gin.Recovery())\n\tapp.Use(func(c *gin.Context) {\n\t\tbody := \"\"\n\t\tif c.Request.Body != nil {\n\t\t\trb, err := c.GetRawData()\n\t\t\tdtmimp.E2P(err)\n\t\t\tif len(rb) > 0 {\n\t\t\t\tbody = string(rb)\n\t\t\t\tc.Request.Body = ioutil.NopCloser(bytes.NewBuffer(rb))\n\t\t\t}\n\t\t}\n\t\tlogger.Debugf(\"begin %s %s body: %s\", c.Request.Method, c.Request.URL, body)\n\t\tc.Next()\n\t})\n\tapp.Any(\"/api/ping\", func(c *gin.Context) { c.JSON(200, map[string]interface{}{\"msg\": \"pong\"}) })\n\treturn app\n}\n\n// WrapHandler used by examples. much more simpler than WrapHandler2\nfunc WrapHandler(fn func(*gin.Context) interface{}) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tbegan := time.Now()\n\t\tret := fn(c)\n\t\tstatus, res := dtmcli.Result2HttpJSON(ret)\n\n\t\tb, _ := json.Marshal(res)\n\t\tif status == http.StatusOK || status == http.StatusTooEarly {\n\t\t\tlogger.Infof(\"%2dms %d %s %s %s\", time.Since(began).Milliseconds(), status, c.Request.Method, c.Request.RequestURI, string(b))\n\t\t} else {\n\t\t\tlogger.Errorf(\"%2dms %d %s %s %s\", time.Since(began).Milliseconds(), status, c.Request.Method, c.Request.RequestURI, string(b))\n\t\t}\n\t\tc.JSON(status, res)\n\t}\n}\n\n// WrapHandler2 wrap a function to be the handler of gin request\n// used by dtmsvr\nfunc WrapHandler2(fn func(*gin.Context) interface{}) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tbegan := time.Now()\n\t\tvar err error\n\t\tr := func() interface{} {\n\t\t\tdefer dtmimp.P2E(&err)\n\t\t\treturn fn(c)\n\t\t}()\n\n\t\tstatus := http.StatusOK\n\n\t\t// in dtm test/busi, there are some functions, which will return a resty response\n\t\t// pass resty response as gin's response\n\t\tif resp, ok := r.(*resty.Response); ok {\n\t\t\tb := resp.Body()\n\t\t\tstatus = resp.StatusCode()\n\t\t\tr = nil\n\t\t\terr = json.Unmarshal(b, &r)\n\t\t}\n\n\t\t// error maybe returned in r, assign it to err\n\t\tif ne, ok := r.(error); ok && err == nil {\n\t\t\terr = ne\n\t\t}\n\n\t\t// if err != nil || r == nil. then set the status and dtm_result\n\t\t// dtm_result is for compatible with version lower than v1.10\n\t\t// when >= v1.10, result test should base on status, not dtm_result.\n\t\tresult := map[string]interface{}{}\n\t\tif err != nil {\n\t\t\tif errors.Is(err, dtmcli.ErrFailure) {\n\t\t\t\tstatus = http.StatusConflict\n\t\t\t\tresult[\"dtm_result\"] = dtmcli.ResultFailure\n\t\t\t} else if errors.Is(err, dtmcli.ErrOngoing) {\n\t\t\t\tstatus = http.StatusTooEarly\n\t\t\t\tresult[\"dtm_result\"] = dtmcli.ResultOngoing\n\t\t\t} else if err != nil {\n\t\t\t\tstatus = http.StatusInternalServerError\n\t\t\t}\n\t\t\tresult[\"message\"] = err.Error()\n\t\t\tr = result\n\t\t} else if r == nil {\n\t\t\tresult[\"dtm_result\"] = dtmcli.ResultSuccess\n\t\t\tr = result\n\t\t}\n\n\t\tb, _ := json.Marshal(r)\n\t\tcont := string(b)\n\t\tif status == http.StatusOK || status == http.StatusTooEarly {\n\t\t\tlogger.Infof(\"%2dms %d %s %s %s\", time.Since(began).Milliseconds(), status, c.Request.Method, c.Request.RequestURI, cont)\n\t\t} else {\n\t\t\tlogger.Errorf(\"%2dms %d %s %s %s\", time.Since(began).Milliseconds(), status, c.Request.Method, c.Request.RequestURI, cont)\n\t\t}\n\t\tc.JSON(status, r)\n\t}\n}\n\n// MustGetwd must version of os.Getwd\nfunc MustGetwd() string {\n\twd, err := os.Getwd()\n\tdtmimp.E2P(err)\n\treturn wd\n}\n\n// GetSQLDir get sql scripts dir, used in test\nfunc GetSQLDir() string {\n\twd := MustGetwd()\n\tif filepath.Base(wd) == \"test\" {\n\t\twd = filepath.Dir(wd)\n\t}\n\treturn wd + \"/sqls\"\n}\n\n// RecoverPanic execs recovery operation\nfunc RecoverPanic(err *error) {\n\tif x := recover(); x != nil {\n\t\te := dtmimp.AsError(x)\n\t\tif err != nil {\n\t\t\t*err = e\n\t\t}\n\t}\n}\n\n// GetNextTime gets next time from second\nfunc GetNextTime(second int64) *time.Time {\n\tnext := time.Now().Add(time.Duration(second) * time.Second)\n\treturn &next\n}\n\n// RunSQLScript 1\nfunc RunSQLScript(conf dtmcli.DBConf, script string, skipDrop bool) {\n\tcon, err := dtmimp.StandaloneDB(conf)\n\tlogger.FatalIfError(err)\n\tdefer func() { _ = con.Close() }()\n\tcontent, err := ioutil.ReadFile(script)\n\tlogger.FatalIfError(err)\n\tsqls := strings.Split(string(content), \";\")\n\tfor _, sql := range sqls {\n\t\ts := strings.TrimSpace(sql)\n\t\tif s == \"\" || (skipDrop && strings.Contains(s, \"drop\")) {\n\t\t\tcontinue\n\t\t}\n\t\t_, err = dtmimp.DBExec(conf.Driver, con, s)\n\t\tlogger.FatalIfError(err)\n\t\tlogger.Infof(\"sql scripts finished: %s\", s)\n\t}\n}\n"
  },
  {
    "path": "dtmutil/utils_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage dtmutil\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestGin(t *testing.T) {\n\tapp := GetGinApp()\n\tapp.GET(\"/api/sample\", WrapHandler2(func(c *gin.Context) interface{} {\n\t\treturn 1\n\t}))\n\tapp.GET(\"/api/error\", WrapHandler2(func(c *gin.Context) interface{} {\n\t\treturn errors.New(\"err1\")\n\t}))\n\tgetResultString := func(api string, body io.Reader) string {\n\t\treq, _ := http.NewRequest(\"GET\", api, body)\n\t\tw := httptest.NewRecorder()\n\t\tapp.ServeHTTP(w, req)\n\t\treturn w.Body.String()\n\t}\n\tassert.Equal(t, \"{\\\"msg\\\":\\\"pong\\\"}\", getResultString(\"/api/ping\", nil))\n\tassert.Equal(t, \"1\", getResultString(\"/api/sample\", nil))\n\tassert.Equal(t, \"{\\\"message\\\":\\\"err1\\\"}\", getResultString(\"/api/error\", strings.NewReader(\"{}\")))\n}\n\nfunc TestFuncs(t *testing.T) {\n\twd := MustGetwd()\n\tassert.NotEqual(t, \"\", wd)\n\n\tdir1 := GetSQLDir()\n\tassert.Equal(t, true, strings.HasSuffix(dir1, \"/sqls\"))\n\n}\n\nfunc TestRecoverPanic(t *testing.T) {\n\terr := func() (rerr error) {\n\t\tdefer RecoverPanic(&rerr)\n\t\tpanic(fmt.Errorf(\"an error\"))\n\t}()\n\tassert.Equal(t, \"an error\", err.Error())\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/dtm-labs/dtm\n\ngo 1.22\n\nrequire (\n\tgithub.com/agiledragon/gomonkey/v2 v2.2.0\n\tgithub.com/dtm-labs/dtmdriver v0.0.6\n\tgithub.com/dtm-labs/dtmdriver-dapr v0.0.1\n\tgithub.com/dtm-labs/dtmdriver-ego v0.1.8\n\tgithub.com/dtm-labs/dtmdriver-gozero v0.0.7\n\tgithub.com/dtm-labs/dtmdriver-kratos v0.0.10\n\tgithub.com/dtm-labs/dtmdriver-polaris v0.0.5\n\tgithub.com/dtm-labs/dtmdriver-springcloud v1.2.3\n\tgithub.com/dtm-labs/logger v0.0.2\n\tgithub.com/gin-gonic/gin v1.9.1\n\tgithub.com/redis/go-redis/v9 v9.5.1\n\tgithub.com/go-resty/resty/v2 v2.7.0\n\tgithub.com/go-sql-driver/mysql v1.7.0\n\tgithub.com/lib/pq v1.10.7\n\tgithub.com/lithammer/shortuuid/v3 v3.0.7\n\tgithub.com/onsi/gomega v1.23.0\n\tgithub.com/pkg/errors v0.9.1\n\tgithub.com/prometheus/client_golang v1.16.0\n\tgithub.com/stretchr/testify v1.8.3\n\tgithub.com/zhufuyi/dtmdriver-sponge v0.0.2\n\tgo.etcd.io/bbolt v1.3.6\n\tgo.mongodb.org/mongo-driver v1.11.6\n\tgo.uber.org/automaxprocs v1.5.1\n\tgoogle.golang.org/grpc v1.56.3\n\tgoogle.golang.org/protobuf v1.34.1\n\tgopkg.in/yaml.v3 v3.0.1\n\tgorm.io/driver/mysql v1.0.3\n\tgorm.io/driver/postgres v1.2.1\n\tgorm.io/driver/sqlserver v1.1.2\n\tgorm.io/gorm v1.22.2\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.2.0 // indirect\n\tgithub.com/aliyun/alibaba-cloud-sdk-go v1.61.1800 // indirect\n\tgithub.com/armon/go-metrics v0.4.1 // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/buger/jsonparser v1.1.1 // indirect\n\tgithub.com/bytedance/sonic v1.9.1 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.2.0 // indirect\n\tgithub.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect\n\tgithub.com/coreos/go-semver v0.3.0 // indirect\n\tgithub.com/coreos/go-systemd/v22 v22.3.2 // indirect\n\tgithub.com/dapr/dapr v1.10.9 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/denisenkom/go-mssqldb v0.12.3 // indirect\n\tgithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect\n\tgithub.com/emicklei/go-restful/v3 v3.10.2 // indirect\n\tgithub.com/fatih/color v1.14.1 // indirect\n\tgithub.com/gabriel-vasile/mimetype v1.4.2 // indirect\n\tgithub.com/ghodss/yaml v1.0.0 // indirect\n\tgithub.com/gin-contrib/sse v0.1.0 // indirect\n\tgithub.com/go-errors/errors v1.4.2 // indirect\n\tgithub.com/go-kratos/kratos/contrib/registry/consul/v2 v2.0.0-20220414054820-d0b704b8f38d // indirect\n\tgithub.com/go-kratos/kratos/contrib/registry/etcd/v2 v2.0.0-20220301040457-03ad2b663624 // indirect\n\tgithub.com/go-kratos/kratos/v2 v2.2.1 // indirect\n\tgithub.com/go-logr/logr v1.2.3 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.19.5 // indirect\n\tgithub.com/go-openapi/jsonreference v0.20.0 // indirect\n\tgithub.com/go-openapi/swag v0.19.14 // indirect\n\tgithub.com/go-playground/locales v0.14.1 // indirect\n\tgithub.com/go-playground/universal-translator v0.18.1 // indirect\n\tgithub.com/go-playground/validator/v10 v10.14.0 // indirect\n\tgithub.com/goccy/go-json v0.10.2 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect\n\tgithub.com/golang-sql/sqlexp v0.1.0 // indirect\n\tgithub.com/golang/mock v1.6.0 // indirect\n\tgithub.com/golang/protobuf v1.5.3 // indirect\n\tgithub.com/golang/snappy v0.0.4 // indirect\n\tgithub.com/google/gnostic v0.5.7-v3refs // indirect\n\tgithub.com/google/go-cmp v0.5.9 // indirect\n\tgithub.com/google/gofuzz v1.2.0 // indirect\n\tgithub.com/google/uuid v1.3.0 // indirect\n\tgithub.com/gotomicro/ego v1.1.5 // indirect\n\tgithub.com/gotomicro/ego-component/eetcd v0.2.3 // indirect\n\tgithub.com/gotomicro/ego-component/ek8s v0.2.3 // indirect\n\tgithub.com/gotomicro/logrotate v0.0.0-20211108034117-46d53eedc960 // indirect\n\tgithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect\n\tgithub.com/hashicorp/consul/api v1.19.1 // indirect\n\tgithub.com/hashicorp/errwrap v1.1.0 // indirect\n\tgithub.com/hashicorp/go-cleanhttp v0.5.2 // indirect\n\tgithub.com/hashicorp/go-hclog v1.4.0 // indirect\n\tgithub.com/hashicorp/go-immutable-radix v1.3.1 // indirect\n\tgithub.com/hashicorp/go-multierror v1.1.1 // indirect\n\tgithub.com/hashicorp/go-rootcerts v1.0.2 // indirect\n\tgithub.com/hashicorp/golang-lru v0.5.4 // indirect\n\tgithub.com/hashicorp/serf v0.10.1 // indirect\n\tgithub.com/jackc/chunkreader/v2 v2.0.1 // indirect\n\tgithub.com/jackc/pgconn v1.14.3 // indirect\n\tgithub.com/jackc/pgio v1.0.0 // indirect\n\tgithub.com/jackc/pgpassfile v1.0.0 // indirect\n\tgithub.com/jackc/pgproto3/v2 v2.3.3 // indirect\n\tgithub.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect\n\tgithub.com/jackc/pgtype v1.14.0 // indirect\n\tgithub.com/jackc/pgx/v4 v4.18.2 // indirect\n\tgithub.com/jinzhu/inflection v1.0.0 // indirect\n\tgithub.com/jinzhu/now v1.1.2 // indirect\n\tgithub.com/jmespath/go-jmespath v0.4.0 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/jpillora/backoff v1.0.0 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/klauspost/compress v1.15.14 // indirect\n\tgithub.com/klauspost/cpuid/v2 v2.2.4 // indirect\n\tgithub.com/leodido/go-urn v1.2.4 // indirect\n\tgithub.com/mailru/easyjson v0.7.7 // indirect\n\tgithub.com/mattn/go-colorable v0.1.13 // indirect\n\tgithub.com/mattn/go-isatty v0.0.19 // indirect\n\tgithub.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect\n\tgithub.com/mitchellh/go-homedir v1.1.0 // indirect\n\tgithub.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/montanaflynn/stats v0.6.6 // indirect\n\tgithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect\n\tgithub.com/nacos-group/nacos-sdk-go v1.1.1 // indirect\n\tgithub.com/nacos-group/nacos-sdk-go/v2 v2.1.0 // indirect\n\tgithub.com/natefinch/lumberjack v2.0.0+incompatible // indirect\n\tgithub.com/pelletier/go-toml/v2 v2.0.8 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/polarismesh/grpc-go-polaris v1.2.1-0.20220306155244-f0b83ba62878 // indirect\n\tgithub.com/polarismesh/polaris-go v1.1.0 // indirect\n\tgithub.com/prometheus/client_model v0.3.0 // indirect\n\tgithub.com/prometheus/common v0.42.0 // indirect\n\tgithub.com/prometheus/procfs v0.10.1 // indirect\n\tgithub.com/spaolacci/murmur3 v1.1.0 // indirect\n\tgithub.com/spf13/cast v1.5.0 // indirect\n\tgithub.com/twitchyliquid64/golang-asm v0.15.1 // indirect\n\tgithub.com/ugorji/go/codec v1.2.11 // indirect\n\tgithub.com/xdg-go/pbkdf2 v1.0.0 // indirect\n\tgithub.com/xdg-go/scram v1.1.2 // indirect\n\tgithub.com/xdg-go/stringprep v1.0.4 // indirect\n\tgithub.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect\n\tgithub.com/zeromicro/go-zero v1.4.4 // indirect\n\tgithub.com/zeromicro/zero-contrib/zrpc/registry/consul v0.0.0-20230212061721-86dbe4a9e613 // indirect\n\tgithub.com/zeromicro/zero-contrib/zrpc/registry/nacos v0.0.0-20220525162615-f10f16d580d6 // indirect\n\tgo.etcd.io/etcd/api/v3 v3.5.5 // indirect\n\tgo.etcd.io/etcd/client/pkg/v3 v3.5.5 // indirect\n\tgo.etcd.io/etcd/client/v3 v3.5.5 // indirect\n\tgo.opentelemetry.io/otel v1.14.0 // indirect\n\tgo.opentelemetry.io/otel/trace v1.14.0 // indirect\n\tgo.uber.org/atomic v1.10.0 // indirect\n\tgo.uber.org/multierr v1.9.0 // indirect\n\tgo.uber.org/zap v1.24.0 // indirect\n\tgolang.org/x/arch v0.3.0 // indirect\n\tgolang.org/x/crypto v0.20.0 // indirect\n\tgolang.org/x/net v0.21.0 // indirect\n\tgolang.org/x/oauth2 v0.7.0 // indirect\n\tgolang.org/x/sync v0.2.0 // indirect\n\tgolang.org/x/sys v0.17.0 // indirect\n\tgolang.org/x/term v0.17.0 // indirect\n\tgolang.org/x/text v0.14.0 // indirect\n\tgolang.org/x/time v0.3.0 // indirect\n\tgoogle.golang.org/appengine v1.6.7 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect\n\tgopkg.in/inf.v0 v0.9.1 // indirect\n\tgopkg.in/ini.v1 v1.66.6 // indirect\n\tgopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tk8s.io/api v0.26.1 // indirect\n\tk8s.io/apimachinery v0.26.1 // indirect\n\tk8s.io/client-go v0.26.1 // indirect\n\tk8s.io/klog/v2 v2.80.1 // indirect\n\tk8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect\n\tk8s.io/utils v0.0.0-20230115233650-391b47cb4029 // indirect\n\tsigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect\n\tsigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect\n\tsigs.k8s.io/yaml v1.3.0 // indirect\n)\n\nretract v1.18.7\n\n// replace github.com/dtm-labs/dtmdriver v0.0.2 => /Users/wangxi/dtm/dtmdriver\n\n// replace github.com/dtm-labs/dtmdriver-http => /Users/wangxi/dtm/dtmdriver-http-nacos\n"
  },
  {
    "path": "go.sum",
    "content": "bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=\nbazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM=\ncloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=\ncloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=\ncloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=\ncloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=\ncloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=\ncloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=\ncloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=\ncloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=\ncloud.google.com/go v0.86.0/go.mod h1:YG2MRW8zzPSZaztnTZtxbMPK2VYaHg4NTDYZMG+5ZqQ=\ncloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=\ncloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=\ncloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=\ncloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=\ncloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=\ncloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=\ncloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=\ncloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U=\ncloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=\ncloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=\ncloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU=\ncloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=\ncloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM=\ncloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I=\ncloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4=\ncloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw=\ncloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o=\ncloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE=\ncloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw=\ncloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY=\ncloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg=\ncloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI=\ncloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4=\ncloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk=\ncloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc=\ncloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc=\ncloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04=\ncloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno=\ncloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak=\ncloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4=\ncloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0=\ncloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ=\ncloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk=\ncloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0=\ncloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc=\ncloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o=\ncloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s=\ncloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0=\ncloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ=\ncloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY=\ncloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY=\ncloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw=\ncloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI=\ncloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo=\ncloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0=\ncloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0=\ncloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8=\ncloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8=\ncloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM=\ncloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc=\ncloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI=\ncloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE=\ncloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE=\ncloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4=\ncloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA=\ncloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw=\ncloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc=\ncloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY=\ncloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s=\ncloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI=\ncloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y=\ncloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM=\ncloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI=\ncloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0=\ncloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk=\ncloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg=\ncloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590=\ncloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk=\ncloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk=\ncloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U=\ncloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA=\ncloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM=\ncloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk=\ncloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY=\ncloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI=\ncloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4=\ncloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI=\ncloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=\ncloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=\ncloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=\ncloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=\ncloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=\ncloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=\ncloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU=\ncloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU=\ncloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU=\ncloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE=\ncloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo=\ncloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA=\ncloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU=\ncloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=\ncloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=\ncloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=\ncloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY=\ncloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck=\ncloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg=\ncloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo=\ncloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I=\ncloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4=\ncloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0=\ncloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs=\ncloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc=\ncloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE=\ncloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM=\ncloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM=\ncloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ=\ncloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo=\ncloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE=\ncloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0=\ncloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38=\ncloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w=\ncloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I=\ncloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ=\ncloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA=\ncloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A=\ncloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s=\ncloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI=\ncloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo=\ncloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM=\ncloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo=\ncloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ=\ncloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g=\ncloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4=\ncloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c=\ncloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s=\ncloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4=\ncloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0=\ncloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8=\ncloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek=\ncloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0=\ncloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM=\ncloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q=\ncloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU=\ncloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU=\ncloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k=\ncloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4=\ncloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y=\ncloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg=\ncloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk=\ncloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w=\ncloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU=\ncloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI=\ncloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8=\ncloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc=\ncloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw=\ncloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w=\ncloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=\ncloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE=\ncloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk=\ncloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg=\ncloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY=\ncloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08=\ncloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM=\ncloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA=\ncloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w=\ncloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM=\ncloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60=\ncloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo=\ncloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o=\ncloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A=\ncloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0=\ncloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0=\ncloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA=\ncloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI=\ncloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc=\ncloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM=\ncloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o=\ncloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c=\ncloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=\ncloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc=\ncloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc=\ncloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg=\ncloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE=\ncloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc=\ncloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A=\ncloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM=\ncloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY=\ncloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs=\ncloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g=\ncloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA=\ncloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg=\ncloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0=\ncloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic=\ncloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI=\ncloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE=\ncloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8=\ncloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8=\ncloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08=\ncloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw=\ncloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE=\ncloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc=\ncloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE=\ncloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM=\ncloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI=\ncloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4=\ncloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w=\ncloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE=\ncloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM=\ncloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA=\ncloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY=\ncloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY=\ncloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s=\ncloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8=\ncloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI=\ncloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk=\ncloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4=\ncloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA=\ncloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o=\ncloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM=\ncloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8=\ncloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8=\ncloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4=\ncloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ=\ncloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU=\ncloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY=\ncloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34=\ncloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA=\ncloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0=\ncloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4=\ncloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs=\ncloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA=\ncloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk=\ncloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE=\ncloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc=\ncloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs=\ncloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg=\ncloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo=\ncloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw=\ncloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E=\ncloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU=\ncloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70=\ncloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo=\ncloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0=\ncloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA=\ncloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg=\ncloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE=\ncloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0=\ncloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/pubsub v1.12.2/go.mod h1:BmI/dqa6eXfm8WTp+JIN6d6vtVGq+vcsnglFKn/aVkY=\ncloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI=\ncloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0=\ncloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg=\ncloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4=\ncloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o=\ncloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk=\ncloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo=\ncloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE=\ncloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U=\ncloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg=\ncloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4=\ncloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg=\ncloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c=\ncloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs=\ncloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70=\ncloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y=\ncloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A=\ncloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA=\ncloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM=\ncloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA=\ncloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0=\ncloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU=\ncloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg=\ncloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4=\ncloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY=\ncloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc=\ncloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y=\ncloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do=\ncloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo=\ncloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s=\ncloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI=\ncloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk=\ncloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44=\ncloud.google.com/go/secretmanager v1.4.0/go.mod h1:h2VZz7Svt1W9/YVl7mfcX9LddvS6SOLOvMoOXBhYT1k=\ncloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA=\ncloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4=\ncloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4=\ncloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4=\ncloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0=\ncloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU=\ncloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q=\ncloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA=\ncloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU=\ncloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc=\ncloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk=\ncloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk=\ncloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU=\ncloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s=\ncloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs=\ncloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg=\ncloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4=\ncloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U=\ncloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco=\ncloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo=\ncloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E=\ncloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU=\ncloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4=\ncloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw=\ncloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos=\ncloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM=\ncloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ=\ncloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0=\ncloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ncloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=\ncloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=\ncloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc=\ncloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s=\ncloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w=\ncloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I=\ncloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw=\ncloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g=\ncloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM=\ncloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA=\ncloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8=\ncloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4=\ncloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ=\ncloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg=\ncloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28=\ncloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y=\ncloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs=\ncloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg=\ncloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk=\ncloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw=\ncloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU=\ncloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4=\ncloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M=\ncloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU=\ncloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0=\ncloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo=\ncloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo=\ncloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY=\ncloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E=\ncloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE=\ncloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g=\ncloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208=\ncloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w=\ncloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8=\ncloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE=\ncloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg=\ncloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc=\ncloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A=\ncloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo=\ncloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ=\ncloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0=\ncloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=\ncloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M=\ncloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA=\ncode.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c/go.mod h1:QD9Lzhd/ux6eNQVUDVRJX/RKTigpewimNYBi7ivZKY8=\ncontrib.go.opencensus.io/exporter/prometheus v0.4.1/go.mod h1:t9wvfitlUjGXG2IXAZsuFq26mDGid/JwCEXp+gTG/9U=\ncontrib.go.opencensus.io/exporter/zipkin v0.1.1/go.mod h1:GMvdSl3eJ2gapOaLKzTKE3qDgUkJ86k9k3yY2eqwkzc=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ndubbo.apache.org/dubbo-go/v3 v3.0.3-0.20220610080020-48691a404537/go.mod h1:O7eTHAilCWlqBjEkG2MW9khZFImiARb/tSOE8PJas+g=\ngithub.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=\ngithub.com/99designs/keyring v1.1.6/go.mod h1:16e0ds7LGQQcT59QqkTg72Hh5ShM51Byv5PEmW6uoRU=\ngithub.com/99designs/keyring v1.2.0/go.mod h1:ETJn2A9cfvJKq1Q4FeOc+eetK52Ik0kUGog7Uy+xvX8=\ngithub.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg=\ngithub.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a/go.mod h1:C0A1KeiVHs+trY6gUTPhhGammbrZ30ZfXRW/nuT7HLw=\ngithub.com/AthenZ/athenz v1.10.39/go.mod h1:3Tg8HLsiQZp81BJY58JBeU2BR6B/H4/0MQGfCwhHNEA=\ngithub.com/Azure/azure-amqp-common-go/v3 v3.0.1/go.mod h1:PBIGdzcO1teYoufTKMcGibdKaYZv4avS+O6LNIp8bq0=\ngithub.com/Azure/azure-amqp-common-go/v3 v3.2.3/go.mod h1:7rPmbSfszeovxGfc5fSAXE4ehlXQZHpMja2OtxC2Tas=\ngithub.com/Azure/azure-event-hubs-go/v3 v3.3.18/go.mod h1:R5H325+EzgxcBDkUerEwtor7ZQg77G7HiOTwpcuIVXY=\ngithub.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=\ngithub.com/Azure/azure-pipeline-go v0.1.9/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=\ngithub.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=\ngithub.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=\ngithub.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=\ngithub.com/Azure/azure-sdk-for-go v37.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=\ngithub.com/Azure/azure-sdk-for-go v51.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=\ngithub.com/Azure/azure-sdk-for-go v56.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=\ngithub.com/Azure/azure-sdk-for-go v65.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=\ngithub.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=\ngithub.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=\ngithub.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0/go.mod h1:bhXu1AjYL+wutSL/kpSq6s7733q2Rb0yuot9Zgfqa/0=\ngithub.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.0.1/go.mod h1:l3wvZkG9oW07GLBW5Cd0WwG5asOfJ8aqE8raUvNzLpk=\ngithub.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=\ngithub.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=\ngithub.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.7.1/go.mod h1:WcC2Tk6JyRlqjn2byvinNnZzgdXmZ1tOiIOWNh1u0uA=\ngithub.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.5.0/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA=\ngithub.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.0.1/go.mod h1:LH9XQnMr2ZYxQdVdCrzLO9mxeDyrDFa6wbSI3x5zCZk=\ngithub.com/Azure/azure-service-bus-go v0.10.10/go.mod h1:o5z/3lDG1iT/T/G7vgIwIqVDTx9Qa2wndf5OdzSzpF8=\ngithub.com/Azure/azure-storage-blob-go v0.6.0/go.mod h1:oGfmITT1V6x//CswqY2gtAHND+xIP64/qL7a5QJix0Y=\ngithub.com/Azure/azure-storage-blob-go v0.10.0/go.mod h1:ep1edmW+kNQx4UfWM9heESNmQdijykocJ0YOxmMX8SE=\ngithub.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd/go.mod h1:K6am8mT+5iFXgingS9LUc7TmbsW6XBw3nxaRyaMyWc8=\ngithub.com/Azure/go-amqp v0.13.0/go.mod h1:qj+o8xPCz9tMSbQ83Vp8boHahuRDl5mkNHyt1xlxUTs=\ngithub.com/Azure/go-amqp v0.13.1/go.mod h1:qj+o8xPCz9tMSbQ83Vp8boHahuRDl5mkNHyt1xlxUTs=\ngithub.com/Azure/go-amqp v0.17.0/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg=\ngithub.com/Azure/go-amqp v0.17.4/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg=\ngithub.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=\ngithub.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=\ngithub.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=\ngithub.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=\ngithub.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=\ngithub.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=\ngithub.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=\ngithub.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=\ngithub.com/Azure/go-autorest/autorest v0.11.3/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=\ngithub.com/Azure/go-autorest/autorest v0.11.7/go.mod h1:V6p3pKZx1KKkJubbxnDWrzNhEIfOy/pTGasLqzHIPHs=\ngithub.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=\ngithub.com/Azure/go-autorest/autorest v0.11.20/go.mod h1:o3tqFY+QR40VOlk+pV4d77mORO64jOXSgEnPQgLK6JY=\ngithub.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=\ngithub.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U=\ngithub.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=\ngithub.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=\ngithub.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=\ngithub.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.4/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.15/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=\ngithub.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM=\ngithub.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg=\ngithub.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw=\ngithub.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg=\ngithub.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=\ngithub.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=\ngithub.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=\ngithub.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=\ngithub.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=\ngithub.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=\ngithub.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=\ngithub.com/Azure/go-autorest/autorest/validation v0.3.0/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=\ngithub.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=\ngithub.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=\ngithub.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=\ngithub.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=\ngithub.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=\ngithub.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=\ngithub.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=\ngithub.com/AzureAD/microsoft-authentication-library-for-go v0.5.1/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=\ngithub.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=\ngithub.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=\ngithub.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/ClickHouse/clickhouse-go v1.5.1/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=\ngithub.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=\ngithub.com/ClickHouse/clickhouse-go/v2 v2.0.14/go.mod h1:iq2DUGgpA4BBki2CVwrF8x43zqBjdgHtbexkFkh5a6M=\ngithub.com/ClickHouse/clickhouse-go/v2 v2.2.0/go.mod h1:8f2XZUi7XoeU+uPIytSi1cvx8fmJxi7vIgqpvYTF1+o=\ngithub.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=\ngithub.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=\ngithub.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=\ngithub.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=\ngithub.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=\ngithub.com/Flaque/filet v0.0.0-20201012163910-45f684403088/go.mod h1:TK+jB3mBs+8ZMWhU5BqZKnZWJ1MrLo8etNVg51ueTBo=\ngithub.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=\ngithub.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=\ngithub.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=\ngithub.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=\ngithub.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=\ngithub.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=\ngithub.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=\ngithub.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=\ngithub.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=\ngithub.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=\ngithub.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=\ngithub.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=\ngithub.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=\ngithub.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=\ngithub.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=\ngithub.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=\ngithub.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=\ngithub.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=\ngithub.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=\ngithub.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00=\ngithub.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600=\ngithub.com/Microsoft/hcsshim v0.8.20/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4=\ngithub.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4=\ngithub.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg=\ngithub.com/Microsoft/hcsshim v0.9.1/go.mod h1:Y/0uV2jUab5kBI7SQgl62at0AVX7uaruzADAVmxm3eM=\ngithub.com/Microsoft/hcsshim v0.9.2/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc=\ngithub.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=\ngithub.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=\ngithub.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=\ngithub.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=\ngithub.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=\ngithub.com/RaMin0/gin-health-check v0.0.0-20180807004848-a677317b3f01/go.mod h1:vZ/F780spvlix7Qg0/17Uj0SayI+CqtybQHtPEV9RTE=\ngithub.com/RoaringBitmap/roaring v1.1.0/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA=\ngithub.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=\ngithub.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=\ngithub.com/Shopify/sarama v1.23.1/go.mod h1:XLH1GYJnLVE0XCr6KdJGVJRTwY30moWNJ4sERjXX6fs=\ngithub.com/Shopify/sarama v1.30.0/go.mod h1:zujlQQx1kzHsh4jfV1USnptCQrHAEZ2Hk8fTKCulPVs=\ngithub.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=\ngithub.com/Shopify/toxiproxy/v2 v2.1.6-0.20210914104332-15ea381dcdae/go.mod h1:/cvHQkZ1fst0EmZnA5dFtiQdWCNCFYzb+uE2vqVgvx0=\ngithub.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=\ngithub.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=\ngithub.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=\ngithub.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=\ngithub.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA=\ngithub.com/a8m/documentdb v1.3.1-0.20220405205223-5b41ba0aaeb1/go.mod h1:4Z0mpi7fkyqjxUdGiNMO3vagyiUoiwLncaIX6AsW5z0=\ngithub.com/aerospike/aerospike-client-go v4.5.0+incompatible/go.mod h1:zj8LBEnWBDOVEIJt8LvaRvDG5ARAoa5dBeHaB472NRc=\ngithub.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=\ngithub.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw=\ngithub.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=\ngithub.com/agiledragon/gomonkey/v2 v2.2.0 h1:QJWqpdEhGV/JJy70sZ/LDnhbSlMrqHAWHcNOjz1kyuI=\ngithub.com/agiledragon/gomonkey/v2 v2.2.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=\ngithub.com/agrea/ptr v0.0.0-20180711073057-77a518d99b7b/go.mod h1:Tie46d3UWzXpj+Fh9+DQTyaUxEpFBPOLXrnx7nxlKRo=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=\ngithub.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=\ngithub.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk=\ngithub.com/alibaba/sentinel-golang v1.0.3/go.mod h1:Lag5rIYyJiPOylK8Kku2P+a23gdKMMqzQS7wTnjWEpk=\ngithub.com/alibaba/sentinel-golang v1.0.4/go.mod h1:Lag5rIYyJiPOylK8Kku2P+a23gdKMMqzQS7wTnjWEpk=\ngithub.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=\ngithub.com/alibabacloud-go/darabonba-openapi v0.1.4/go.mod h1:j03z4XUkIC9aBj/w5Bt7H0cygmPNt5sug8NXle68+Og=\ngithub.com/alibabacloud-go/darabonba-openapi v0.1.16/go.mod h1:ZjyqRbbZOaUBSh7keeH8VQN/BzCPvxCQwMuJGDdbmXQ=\ngithub.com/alibabacloud-go/darabonba-string v1.0.0/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA=\ngithub.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY=\ngithub.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=\ngithub.com/alibabacloud-go/oos-20190601 v1.0.1/go.mod h1:t7g1ubvGwLe0cP+uLSrTza2S6xthOFZw43h9Zajt+Kw=\ngithub.com/alibabacloud-go/openapi-util v0.0.7/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=\ngithub.com/alibabacloud-go/openapi-util v0.0.10/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=\ngithub.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg=\ngithub.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=\ngithub.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=\ngithub.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=\ngithub.com/alibabacloud-go/tea v1.1.15/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=\ngithub.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=\ngithub.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=\ngithub.com/alibabacloud-go/tea-utils v1.3.9/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=\ngithub.com/alibabacloud-go/tea-utils v1.4.3/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw=\ngithub.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=\ngithub.com/alicebob/miniredis/v2 v2.13.3/go.mod h1:uS970Sw5Gs9/iK3yBg0l9Uj9s25wXxSpQUE9EaJ/Blg=\ngithub.com/alicebob/miniredis/v2 v2.17.0/go.mod h1:gquAfGbzn92jvtrSC69+6zZnwSODVXVpYDRaGhWaL6I=\ngithub.com/alicebob/miniredis/v2 v2.22.0/go.mod h1:XNqvJdQJv5mSuVMc0ynneafpnL/zv52acZ6kqeS0t88=\ngithub.com/alicebob/miniredis/v2 v2.30.0/go.mod h1:84TWKZlxYkfgMucPBf5SOQBYJceZeQRFIaQgNMiCX6Q=\ngithub.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk=\ngithub.com/aliyun/alibaba-cloud-sdk-go v1.61.1402/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=\ngithub.com/aliyun/alibaba-cloud-sdk-go v1.61.1620/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=\ngithub.com/aliyun/alibaba-cloud-sdk-go v1.61.1704/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=\ngithub.com/aliyun/alibaba-cloud-sdk-go v1.61.1800 h1:ie/8RxBOfKZWcrbYSJi2Z8uX8TcOlSMwPlEJh83OeOw=\ngithub.com/aliyun/alibaba-cloud-sdk-go v1.61.1800/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=\ngithub.com/aliyun/aliyun-oss-go-sdk v2.0.7+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=\ngithub.com/aliyun/aliyun-tablestore-go-sdk v1.6.0/go.mod h1:jixoiNNRR/4ziq0yub1fTlxmDcQwlpkaujpaWIATQWM=\ngithub.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=\ngithub.com/aliyunmq/mq-http-go-sdk v1.0.3/go.mod h1:JYfRMQoPexERvnNNBcal0ZQ2TVQ5ialDiW9ScjaadEM=\ngithub.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=\ngithub.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=\ngithub.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=\ngithub.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220418222510-f25a4f6275ed/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=\ngithub.com/apache/dubbo-getty v1.4.9-0.20220610060150-8af010f3f3dc/go.mod h1:cPJlbcHUTNTpiboMQjMHhE9XBni11LiBiG8FdrDuVzk=\ngithub.com/apache/dubbo-go-hessian2 v1.9.1/go.mod h1:xQUjE7F8PX49nm80kChFvepA/AvqAZ0oh/UaB6+6pBE=\ngithub.com/apache/dubbo-go-hessian2 v1.9.3/go.mod h1:xQUjE7F8PX49nm80kChFvepA/AvqAZ0oh/UaB6+6pBE=\ngithub.com/apache/dubbo-go-hessian2 v1.11.0/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w=\ngithub.com/apache/pulsar-client-go v0.8.1/go.mod h1:yJNcvn/IurarFDxwmoZvb2Ieylg630ifxeO/iXpk27I=\ngithub.com/apache/pulsar-client-go/oauth2 v0.0.0-20220120090717-25e59572242e/go.mod h1:Xee4tgYLFpYcPMcTfBYWE1uKRzeciodGTSEDMzsR6i8=\ngithub.com/apache/rocketmq-client-go v1.2.5/go.mod h1:Kap8oXIVLlHF50BGUbN9z97QUp1GaK1nOoCfsZnR2bw=\ngithub.com/apache/rocketmq-client-go/v2 v2.1.0/go.mod h1:oEZKFDvS7sz/RWU0839+dQBupazyBV7WX5cP6nrio0Q=\ngithub.com/apache/rocketmq-client-go/v2 v2.1.1-rc2/go.mod h1:DDYjQ9wxYmJLjgNK4+RqyFE8/13gLK/Bugz4U6zD5MI=\ngithub.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=\ngithub.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=\ngithub.com/apache/thrift v0.14.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=\ngithub.com/appscode/go-querystring v0.0.0-20170504095604-0126cfb3f1dc/go.mod h1:w648aMHEgFYS6xb0KVMMtZ2uMeemhiKCuD2vj6gY52A=\ngithub.com/ardielle/ardielle-go v1.5.2/go.mod h1:I4hy1n795cUhaVt/ojz83SNVCYIGsAFAONtv2Dr7HUI=\ngithub.com/ardielle/ardielle-tools v1.5.4/go.mod h1:oZN+JRMnqGiIhrzkRN9l26Cej9dEx4jeNG6A+AdkShk=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=\ngithub.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=\ngithub.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=\ngithub.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=\ngithub.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=\ngithub.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=\ngithub.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef/go.mod h1:JS7hed4L1fj0hXcyEejnW57/7LCetXggd+vwrRnYeII=\ngithub.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=\ngithub.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=\ngithub.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=\ngithub.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=\ngithub.com/aws/aws-sdk-go v1.19.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=\ngithub.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=\ngithub.com/aws/aws-sdk-go v1.32.6/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=\ngithub.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=\ngithub.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=\ngithub.com/aws/aws-sdk-go v1.41.7/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=\ngithub.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=\ngithub.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=\ngithub.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw=\ngithub.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ=\ngithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM=\ngithub.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ=\ngithub.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY=\ngithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8=\ngithub.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk=\ngithub.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g=\ngithub.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=\ngithub.com/awslabs/kinesis-aggregation/go v0.0.0-20210630091500-54e17340d32f/go.mod h1:SghidfnxvX7ribW6nHI7T+IBbc9puZ9kk5Tx/88h8P4=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=\ngithub.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0/go.mod h1:6YNgTHLutezwnBvyneBbwvB8C82y3dcoOj5EQJIdGXA=\ngithub.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=\ngithub.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=\ngithub.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=\ngithub.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=\ngithub.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=\ngithub.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=\ngithub.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=\ngithub.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=\ngithub.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=\ngithub.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=\ngithub.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=\ngithub.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q=\ngithub.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=\ngithub.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=\ngithub.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=\ngithub.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=\ngithub.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=\ngithub.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=\ngithub.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=\ngithub.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=\ngithub.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=\ngithub.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=\ngithub.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=\ngithub.com/bytecodealliance/wasmtime-go v0.35.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI=\ngithub.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=\ngithub.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=\ngithub.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=\ngithub.com/camunda/zeebe/clients/go/v8 v8.0.3/go.mod h1:iOEgFlCYAPdqae6iPp0ajeo2RSxJirU39i+UAN74NOY=\ngithub.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=\ngithub.com/cenkalti/backoff v2.0.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=\ngithub.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=\ngithub.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=\ngithub.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=\ngithub.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=\ngithub.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=\ngithub.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=\ngithub.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=\ngithub.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=\ngithub.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=\ngithub.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=\ngithub.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=\ngithub.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc=\ngithub.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=\ngithub.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=\ngithub.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=\ngithub.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=\ngithub.com/cinience/go_rocketmq v0.0.2/go.mod h1:2YNY7emT546dcFpMEWLesmAEi4ndW7+tX5VfNf1Zsgs=\ngithub.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=\ngithub.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=\ngithub.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=\ngithub.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=\ngithub.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=\ngithub.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=\ngithub.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=\ngithub.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=\ngithub.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=\ngithub.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=\ngithub.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=\ngithub.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=\ngithub.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=\ngithub.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=\ngithub.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E=\ngithub.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=\ngithub.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=\ngithub.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI=\ngithub.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=\ngithub.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=\ngithub.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=\ngithub.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=\ngithub.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=\ngithub.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU=\ngithub.com/containerd/cgroups v1.0.2/go.mod h1:qpbpJ1jmlqsR9f2IyaLPsdkCdnt0rbDVqIDlhuu5tRY=\ngithub.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8=\ngithub.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=\ngithub.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=\ngithub.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=\ngithub.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=\ngithub.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=\ngithub.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=\ngithub.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=\ngithub.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=\ngithub.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=\ngithub.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=\ngithub.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=\ngithub.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=\ngithub.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=\ngithub.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=\ngithub.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=\ngithub.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ=\ngithub.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU=\ngithub.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI=\ngithub.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s=\ngithub.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=\ngithub.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c=\ngithub.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s=\ngithub.com/containerd/containerd v1.5.9/go.mod h1:fvQqCfadDGga5HZyn3j4+dx56qj2I9YwBrlSdalvJYQ=\ngithub.com/containerd/containerd v1.6.2/go.mod h1:sidY30/InSE1j2vdD1ihtKoJz+lWdaXMdiAeIupaf+s=\ngithub.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=\ngithub.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=\ngithub.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=\ngithub.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo=\ngithub.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y=\ngithub.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=\ngithub.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM=\ngithub.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk=\ngithub.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=\ngithub.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=\ngithub.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=\ngithub.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=\ngithub.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=\ngithub.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=\ngithub.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU=\ngithub.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk=\ngithub.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA=\ngithub.com/containerd/go-cni v1.1.3/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA=\ngithub.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=\ngithub.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=\ngithub.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=\ngithub.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=\ngithub.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=\ngithub.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0=\ngithub.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA=\ngithub.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow=\ngithub.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms=\ngithub.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4=\ngithub.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c=\ngithub.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=\ngithub.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=\ngithub.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM=\ngithub.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=\ngithub.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=\ngithub.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8=\ngithub.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=\ngithub.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=\ngithub.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ=\ngithub.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=\ngithub.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk=\ngithub.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=\ngithub.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s=\ngithub.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw=\ngithub.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y=\ngithub.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=\ngithub.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=\ngithub.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=\ngithub.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=\ngithub.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=\ngithub.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=\ngithub.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y=\ngithub.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM=\ngithub.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8=\ngithub.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE=\ngithub.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=\ngithub.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4=\ngithub.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY=\ngithub.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=\ngithub.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=\ngithub.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=\ngithub.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=\ngithub.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=\ngithub.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=\ngithub.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=\ngithub.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=\ngithub.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=\ngithub.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/creasty/defaults v1.5.2/go.mod h1:FPZ+Y0WNrbqOVw+c6av63eyHUAl6pMHZwqLPvXUZGfY=\ngithub.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=\ngithub.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=\ngithub.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=\ngithub.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=\ngithub.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=\ngithub.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=\ngithub.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=\ngithub.com/dancannon/gorethink v4.0.0+incompatible/go.mod h1:BLvkat9KmZc1efyYwhz3WnybhRZtgF1K929FD8z1avU=\ngithub.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U=\ngithub.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=\ngithub.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=\ngithub.com/dapr/components-contrib v1.8.1-rc.1/go.mod h1:gxrCpaosbI0n3SFW7fKSvJU/ymjryHqrdRgqmsknuno=\ngithub.com/dapr/dapr v1.8.2/go.mod h1:/0JyKebxzz0vPwYXc/2qHBXIicUi01HUWnpQ8AiJ0zM=\ngithub.com/dapr/dapr v1.10.9 h1:oQm9TPImutbf0xEFal+X043m7rU9UeZ1ggTvb5a9EeI=\ngithub.com/dapr/dapr v1.10.9/go.mod h1:G1m5IQ2FgrJWh78FuITR+e42bBiXtnucT/aSwmW5SV8=\ngithub.com/dapr/kit v0.0.2-0.20210614175626-b9074b64d233/go.mod h1:y8r0VqUNKyd6xBXp7gQjwA59wlCLGfKzL5J8iJsN09w=\ngithub.com/dave/dst v0.26.2/go.mod h1:UMDJuIRPfyUCC78eFuB+SV/WI8oDeyFDvM/JR6NI3IU=\ngithub.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e/go.mod h1:i00+b/gKdIDIxuLDFob7ustLAVqhsZRk2qVZrArELGQ=\ngithub.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=\ngithub.com/dave/jennifer v1.4.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=\ngithub.com/dave/kerr v0.0.0-20170318121727-bc25dd6abe8e/go.mod h1:qZqlPyPvfsDJt+3wHJ1EvSXDuVjFTK0j2p/ca+gtsb8=\ngithub.com/dave/rebecca v0.9.1/go.mod h1:N6XYdMD/OKw3lkF3ywh8Z6wPGuwNFDNtWYEMFWEmXBA=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/deepmap/oapi-codegen v1.3.6/go.mod h1:aBozjEveG+33xPiP55Iw/XbVkhtZHEGLq3nxlX0+hfU=\ngithub.com/deepmap/oapi-codegen v1.8.1/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw=\ngithub.com/denisenkom/go-mssqldb v0.0.0-20210411162248-d9abbec934ba/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=\ngithub.com/denisenkom/go-mssqldb v0.11.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=\ngithub.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw=\ngithub.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo=\ngithub.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=\ngithub.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=\ngithub.com/dghubble/go-twitter v0.0.0-20190719072343-39e5462e111f/go.mod h1:xfg4uS5LEzOj8PgZV7SQYRHbG7jPUnelEiaAVJxmhJE=\ngithub.com/dghubble/oauth1 v0.6.0/go.mod h1:8pFdfPkv/jr8mkChVbNVuJ0suiHe278BtWI4Tk1ujxk=\ngithub.com/dghubble/sling v1.3.0/go.mod h1:XXShWaBWKzNLhu2OxikSNFrlsvowtz4kyRuXUG7oQKY=\ngithub.com/dgraph-io/badger/v3 v3.2103.2/go.mod h1:RHo4/GmYcKKh5Lxu63wLEMHJ70Pac2JqZRYGhlyAo2M=\ngithub.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=\ngithub.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=\ngithub.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/didip/tollbooth v4.0.2+incompatible/go.mod h1:A9b0665CE6l1KmzpDws2++elm/CsuWBMa5Jv4WY0PEY=\ngithub.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=\ngithub.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=\ngithub.com/dimfeld/httptreemux v5.0.1+incompatible/go.mod h1:rbUlSV+CCpv/SuqUTP/8Bk2O3LyUV436/yaRGkhP6Z0=\ngithub.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684/go.mod h1:UfCu3YXJJCI+IdnqGgYP82dk2+Joxmv+mUTVBES6wac=\ngithub.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=\ngithub.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=\ngithub.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=\ngithub.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=\ngithub.com/docker/cli v20.10.11+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=\ngithub.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=\ngithub.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=\ngithub.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=\ngithub.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=\ngithub.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=\ngithub.com/docker/docker v20.10.14+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=\ngithub.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=\ngithub.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c=\ngithub.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=\ngithub.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=\ngithub.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=\ngithub.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=\ngithub.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=\ngithub.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=\ngithub.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=\ngithub.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=\ngithub.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=\ngithub.com/dtm-labs/dtmdriver v0.0.3/go.mod h1:fLiEeD2BPwM9Yq96TfcP9KpbTwFsn5nTxa/PP0jmFuk=\ngithub.com/dtm-labs/dtmdriver v0.0.6 h1:Iz6xnO+hE2TKDHI2TX4BKCzMtgXYgeQFBEGvvaNhbs8=\ngithub.com/dtm-labs/dtmdriver v0.0.6/go.mod h1:V5E1uFsExb6Do32ezpB8bMX6be+izLhkcboniLP5shU=\ngithub.com/dtm-labs/dtmdriver-dapr v0.0.1 h1:r+D52Sq51o1w69mo/3GsPC2yHTet142EeDomKRMxHNw=\ngithub.com/dtm-labs/dtmdriver-dapr v0.0.1/go.mod h1:lvNzE3KEMYFtu8ZAJ7kxZ6s2E4yuHN/F8nPJ14OEtXU=\ngithub.com/dtm-labs/dtmdriver-ego v0.1.8 h1:nS8euCVSg5BSl3QHDIb89uDoRJqjtvcGvWyiPllYji8=\ngithub.com/dtm-labs/dtmdriver-ego v0.1.8/go.mod h1:vvYMNetDVNUtiCAVRy3OutRCiQRT0fEwdWzouCPL9JY=\ngithub.com/dtm-labs/dtmdriver-gozero v0.0.7 h1:EmbP0tFjczBfk+FJaPbK2Ikzyb3V5f0rn2JH1eHCVRo=\ngithub.com/dtm-labs/dtmdriver-gozero v0.0.7/go.mod h1:OLz0vfGkWTWK24V1Ed69lWRqu5ROXNhQM7p/oRxgdgs=\ngithub.com/dtm-labs/dtmdriver-kratos v0.0.10 h1:M46un8T47BIiSmuQSBhAEbqQx6wgYSyY0x7IaAVvJi4=\ngithub.com/dtm-labs/dtmdriver-kratos v0.0.10/go.mod h1:aLy3llapyW89JXAkFT2QLK8SHLnLctsPEBpNVz73rV4=\ngithub.com/dtm-labs/dtmdriver-polaris v0.0.5 h1:vlM3mvkgYv6GkgK49Jx1ESvYTi2Os5OdsLsvfMtrNJw=\ngithub.com/dtm-labs/dtmdriver-polaris v0.0.5/go.mod h1:FYF5ot7LCri5oA0qyvGzDRBZiMw08WlxjmFgzFQhIvo=\ngithub.com/dtm-labs/dtmdriver-springcloud v1.2.3 h1:AutSnngy+inr0PYoAT6pY/4Cw4aUZNq1pX7VN4j7tD8=\ngithub.com/dtm-labs/dtmdriver-springcloud v1.2.3/go.mod h1:sswcxoTofararER63EhBu9O0Ab55w20fYp1KsE1HXww=\ngithub.com/dtm-labs/logger v0.0.1/go.mod h1:0woMQZ6ljx9wZIl7hW8cuV2PRQmwEKxhqYtab7zVNWg=\ngithub.com/dtm-labs/logger v0.0.2 h1:UQQTjDHnZhSbAHwXO9ISva1/AGO+MW9MjztAIzqJ1Tw=\ngithub.com/dtm-labs/logger v0.0.2/go.mod h1:WgJjaTSJ0WmITqMGEWDiaamrxgMkAH8TmwIhykuGugY=\ngithub.com/dubbogo/go-zookeeper v1.0.3/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c=\ngithub.com/dubbogo/go-zookeeper v1.0.4-0.20211212162352-f9d2183d89d5/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c=\ngithub.com/dubbogo/gost v1.9.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=\ngithub.com/dubbogo/gost v1.11.18/go.mod h1:vIcP9rqz2KsXHPjsAwIUtfJIJjppQLQDcYaZTy/61jI=\ngithub.com/dubbogo/gost v1.11.23/go.mod h1:PhJ8+qZJx+Txjx1KthNPuVkCvUca0jRLgKWj/noGgeI=\ngithub.com/dubbogo/gost v1.11.25/go.mod h1:iovrPhv0hyakhQGVr4jwiECBL9HXNuBY4VV3HWK5pM0=\ngithub.com/dubbogo/grpc-go v1.42.9/go.mod h1:F1T9hnUvYGW4JLK1QNriavpOkhusU677ovPzLkk6zHM=\ngithub.com/dubbogo/jsonparser v1.0.1/go.mod h1:tYAtpctvSP/tWw4MeelsowSPgXQRVHHWbqL6ynps8jU=\ngithub.com/dubbogo/net v0.0.4/go.mod h1:1CGOnM7X3he+qgGNqjeADuE5vKZQx/eMSeUkpU3ujIc=\ngithub.com/dubbogo/triple v1.0.9/go.mod h1:1t9me4j4CTvNDcsMZy6/OGarbRyAUSY0tFXGXHCp7Iw=\ngithub.com/dubbogo/triple v1.1.8/go.mod h1:9pgEahtmsY/avYJp3dzUQE8CMMVe1NtGBmUhfICKLJk=\ngithub.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=\ngithub.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU=\ngithub.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=\ngithub.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=\ngithub.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=\ngithub.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=\ngithub.com/eclipse/paho.mqtt.golang v1.3.5/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc=\ngithub.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=\ngithub.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=\ngithub.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=\ngithub.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=\ngithub.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=\ngithub.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE=\ngithub.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=\ngithub.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=\ngithub.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=\ngithub.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/go-control-plane v0.10.0/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=\ngithub.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=\ngithub.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=\ngithub.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=\ngithub.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo=\ngithub.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=\ngithub.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=\ngithub.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=\ngithub.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=\ngithub.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=\ngithub.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=\ngithub.com/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=\ngithub.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=\ngithub.com/fasthttp-contrib/sessions v0.0.0-20160905201309-74f6ac73d5d5/go.mod h1:MQXNGeXkpojWTxbN7vXoE3f7EmlA11MlJbsrJpVBINA=\ngithub.com/fasthttp/router v1.3.8/go.mod h1:DQBvuHvYbn3SUN6pGjwjPbpCNpWfCFc5Ipn/Fj6XxFc=\ngithub.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw=\ngithub.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=\ngithub.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=\ngithub.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=\ngithub.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=\ngithub.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/felixge/fgprof v0.9.1/go.mod h1:7/HK6JFtFaARhIljgP2IV8rJLIoHDoOYoUphsnGvqxE=\ngithub.com/felixge/fgprof v0.9.2/go.mod h1:+VNi+ZXtHIQ6wIw6bUT8nXQRefQflWECoFyRealT5sg=\ngithub.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=\ngithub.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=\ngithub.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=\ngithub.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=\ngithub.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=\ngithub.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4=\ngithub.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=\ngithub.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=\ngithub.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=\ngithub.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=\ngithub.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=\ngithub.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=\ngithub.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=\ngithub.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=\ngithub.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA=\ngithub.com/fullstorydev/grpcurl v1.8.7/go.mod h1:pVtM4qe3CMoLaIzYS8uvTuDj2jVYmXqMUkZeijnXp/E=\ngithub.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=\ngithub.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=\ngithub.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=\ngithub.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=\ngithub.com/getkin/kin-openapi v0.2.0/go.mod h1:V1z9xl9oF5Wt7v32ne4FmiF1alpS4dM6mNzoywPOXlk=\ngithub.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=\ngithub.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=\ngithub.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=\ngithub.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=\ngithub.com/gin-gonic/gin v1.7.1/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=\ngithub.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=\ngithub.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=\ngithub.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=\ngithub.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=\ngithub.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=\ngithub.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=\ngithub.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=\ngithub.com/go-co-op/gocron v1.9.0/go.mod h1:DbJm9kdgr1sEvWpHCA7dFFs/PGHPMil9/97EXCRPr4k=\ngithub.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=\ngithub.com/go-errors/errors v1.4.0/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=\ngithub.com/go-errors/errors v1.4.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=\ngithub.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=\ngithub.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=\ngithub.com/go-ini/ini v1.66.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=\ngithub.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=\ngithub.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=\ngithub.com/go-kratos/aegis v0.1.1/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=\ngithub.com/go-kratos/kratos/contrib/registry/consul/v2 v2.0.0-20220414054820-d0b704b8f38d h1:fLF0ALNq0KKahsDIJM7OcMwI0h+t4s7ONg1OzxIQ+5A=\ngithub.com/go-kratos/kratos/contrib/registry/consul/v2 v2.0.0-20220414054820-d0b704b8f38d/go.mod h1:CFHMR6oi+wIEdqxjH4TwKvPlMWUfRY3SMke9++r/dB8=\ngithub.com/go-kratos/kratos/contrib/registry/etcd/v2 v2.0.0-20220301040457-03ad2b663624 h1:IvBtHmRYmHsB13TP21F1FgEtJuIXIASnucaUZKlvp4E=\ngithub.com/go-kratos/kratos/contrib/registry/etcd/v2 v2.0.0-20220301040457-03ad2b663624/go.mod h1:wHDbj1lp8hvAg7Eq7iNFM4vh5Fl8kWIiH4E1YY+fixs=\ngithub.com/go-kratos/kratos/v2 v2.2.0/go.mod h1:yebXu5KMayLjXZzMTY5HWIPRDwcBehHpiNF/Ot8A2pA=\ngithub.com/go-kratos/kratos/v2 v2.2.1 h1:sm29txvyqiQw4v+MftnYWTMgEBjjzWHjrim8kaTVQWE=\ngithub.com/go-kratos/kratos/v2 v2.2.1/go.mod h1:yebXu5KMayLjXZzMTY5HWIPRDwcBehHpiNF/Ot8A2pA=\ngithub.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=\ngithub.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=\ngithub.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=\ngithub.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=\ngithub.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=\ngithub.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=\ngithub.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=\ngithub.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=\ngithub.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=\ngithub.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\ngithub.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro=\ngithub.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=\ngithub.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=\ngithub.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=\ngithub.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=\ngithub.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=\ngithub.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=\ngithub.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=\ngithub.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=\ngithub.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=\ngithub.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=\ngithub.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=\ngithub.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=\ngithub.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=\ngithub.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=\ngithub.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=\ngithub.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=\ngithub.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=\ngithub.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=\ngithub.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=\ngithub.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=\ngithub.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=\ngithub.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=\ngithub.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=\ngithub.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=\ngithub.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=\ngithub.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=\ngithub.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=\ngithub.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=\ngithub.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=\ngithub.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=\ngithub.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=\ngithub.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=\ngithub.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=\ngithub.com/go-resty/resty/v2 v2.6.0/go.mod h1:PwvJS6hvaPkjtjNg9ph+VrSD92bi5Zq73w/BIH7cC3Q=\ngithub.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=\ngithub.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=\ngithub.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=\ngithub.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=\ngithub.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=\ngithub.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=\ngithub.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=\ngithub.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=\ngithub.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=\ngithub.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=\ngithub.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=\ngithub.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=\ngithub.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=\ngithub.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=\ngithub.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=\ngithub.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=\ngithub.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=\ngithub.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=\ngithub.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=\ngithub.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=\ngithub.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=\ngithub.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=\ngithub.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=\ngithub.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=\ngithub.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=\ngithub.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=\ngithub.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=\ngithub.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=\ngithub.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=\ngithub.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=\ngithub.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=\ngithub.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=\ngithub.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=\ngithub.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=\ngithub.com/gocql/gocql v0.0.0-20210515062232-b7ef815b4556/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY=\ngithub.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=\ngithub.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=\ngithub.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=\ngithub.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=\ngithub.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=\ngithub.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=\ngithub.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=\ngithub.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=\ngithub.com/gogap/errors v0.0.0-20200228125012-531a6449b28c/go.mod h1:tbRYYYC7g/H7QlCeX0Z2zaThWKowF4QQCFIsGgAsqRo=\ngithub.com/gogap/stack v0.0.0-20150131034635-fef68dddd4f8/go.mod h1:6q1WEv2BiAO4FSdwLQTJbWQYAn1/qDNJHUGJNXCj9kM=\ngithub.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=\ngithub.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=\ngithub.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=\ngithub.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=\ngithub.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=\ngithub.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=\ngithub.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=\ngithub.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=\ngithub.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=\ngithub.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=\ngithub.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=\ngithub.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=\ngithub.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=\ngithub.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=\ngithub.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=\ngithub.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=\ngithub.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=\ngithub.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=\ngithub.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=\ngithub.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=\ngithub.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=\ngithub.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=\ngithub.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=\ngithub.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc=\ngithub.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg=\ngithub.com/gonum/integrate v0.0.0-20181209220457-a422b5c0fdf2/go.mod h1:pDgmNM6seYpwvPos3q+zxlXMsbve6mOIPucUnUOrI7Y=\ngithub.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks=\ngithub.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A=\ngithub.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw=\ngithub.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=\ngithub.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=\ngithub.com/google/cel-go v0.9.0/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w=\ngithub.com/google/cel-go v0.11.3/go.mod h1:Av7CU6r6X3YmcHR9GXqVDaEJYfEtSxl6wvIjUQTriCw=\ngithub.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA=\ngithub.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=\ngithub.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=\ngithub.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=\ngithub.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0=\ngithub.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=\ngithub.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=\ngithub.com/google/pprof v0.0.0-20181127221834-b4f47329b966/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200615235658-03e1cf38a040/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=\ngithub.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=\ngithub.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=\ngithub.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=\ngithub.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=\ngithub.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=\ngithub.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=\ngithub.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=\ngithub.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=\ngithub.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo=\ngithub.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY=\ngithub.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=\ngithub.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=\ngithub.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=\ngithub.com/googleapis/gnostic v0.4.0/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=\ngithub.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=\ngithub.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=\ngithub.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=\ngithub.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=\ngithub.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=\ngithub.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0=\ngithub.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=\ngithub.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=\ngithub.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=\ngithub.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=\ngithub.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=\ngithub.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=\ngithub.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=\ngithub.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=\ngithub.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=\ngithub.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=\ngithub.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=\ngithub.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=\ngithub.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=\ngithub.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=\ngithub.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/gotomicro/ego v0.6.14/go.mod h1:M9cmJ6sum7GP0IQ25MxATQjVEYObgam6SS82wp/g7C4=\ngithub.com/gotomicro/ego v0.8.0/go.mod h1:eQDInBi4IrfrwctWZZTQmtWTBYrSVgJ0arwx2WE48ag=\ngithub.com/gotomicro/ego v1.1.5 h1:pmnJ/Raiff+BOfAHg49imEjqN9cLuOk54RU4Z7NjJKs=\ngithub.com/gotomicro/ego v1.1.5/go.mod h1:vHAfoVmngjJCC2eRfP7weWeLxs46QpWok2y7PmU0NF0=\ngithub.com/gotomicro/ego-component/eetcd v0.2.3 h1:spJz3dFPis8UBTDNINweIvxOktIYDrlonrymeIdkm6Y=\ngithub.com/gotomicro/ego-component/eetcd v0.2.3/go.mod h1:8GJIWiHBVKd8oP9BoB4vz50oBF6AsjZhbfog0NwgUHY=\ngithub.com/gotomicro/ego-component/ek8s v0.2.3 h1:C6r9vPQelai7DLN4qAJGz4vPlfr8CqU0vsDA3mJbKNE=\ngithub.com/gotomicro/ego-component/ek8s v0.2.3/go.mod h1:AFg/GrQVZUSZM8/cMmJdyjninblvoaSEpqe5G4ClE/A=\ngithub.com/gotomicro/logrotate v0.0.0-20211108024517-45d1f9a03ff5/go.mod h1:jKlh8i9m79fE8HAO28kYLN70l87bb7olTLuX/Blex/U=\ngithub.com/gotomicro/logrotate v0.0.0-20211108034117-46d53eedc960 h1:vp5ls3l11a1XCaU3pJUBV85PwRW47qybqdYEIWCGLIo=\ngithub.com/gotomicro/logrotate v0.0.0-20211108034117-46d53eedc960/go.mod h1:jKlh8i9m79fE8HAO28kYLN70l87bb7olTLuX/Blex/U=\ngithub.com/grandcat/zeroconf v0.0.0-20190424104450-85eadb44205c/go.mod h1:YjKB0WsLXlMkO9p+wGTCoPIDGRJH0mz7E526PxkQVxI=\ngithub.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=\ngithub.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=\ngithub.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0=\ngithub.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=\ngithub.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=\ngithub.com/hashicorp/consul/api v1.9.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=\ngithub.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=\ngithub.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=\ngithub.com/hashicorp/consul/api v1.18.0/go.mod h1:owRRGJ9M5xReDC5nfT8FTJrNAPbT4NM6p/k+d03q2v4=\ngithub.com/hashicorp/consul/api v1.19.1 h1:GLeK1WD4VIRvt4wRhQKHFudztEkRb8pDs+uRiJgNwes=\ngithub.com/hashicorp/consul/api v1.19.1/go.mod h1:jAt316eYgWGNLJtxkMQrcqRpuDE/kFJdqkEFwRXFv8U=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=\ngithub.com/hashicorp/consul/sdk v0.9.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=\ngithub.com/hashicorp/consul/sdk v0.13.0/go.mod h1:0hs/l5fOVhJy/VdcoaNqUSi2AUs95eF5WKtv+EYIQqE=\ngithub.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY=\ngithub.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=\ngithub.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=\ngithub.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=\ngithub.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=\ngithub.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=\ngithub.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=\ngithub.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=\ngithub.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=\ngithub.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=\ngithub.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=\ngithub.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=\ngithub.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I=\ngithub.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=\ngithub.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-msgpack v1.1.5 h1:9byZdVjKTe5mce63pRVNP1L7UAmdHOTEMGehn6KvJWs=\ngithub.com/hashicorp/go-msgpack v1.1.5/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YNK9NfGLXJ69/4=\ngithub.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=\ngithub.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=\ngithub.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=\ngithub.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=\ngithub.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=\ngithub.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=\ngithub.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=\ngithub.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=\ngithub.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=\ngithub.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw=\ngithub.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I=\ngithub.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=\ngithub.com/hashicorp/go-secure-stdlib/password v0.1.1/go.mod h1:9hH302QllNwu1o2TGYtSk8I8kTAN0ca1EHpwhm5Mmzo=\ngithub.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=\ngithub.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=\ngithub.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=\ngithub.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=\ngithub.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=\ngithub.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=\ngithub.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=\ngithub.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=\ngithub.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM=\ngithub.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=\ngithub.com/hashicorp/raft v1.2.0/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8=\ngithub.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=\ngithub.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=\ngithub.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=\ngithub.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=\ngithub.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=\ngithub.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q=\ngithub.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=\ngithub.com/hashicorp/vault/sdk v0.3.0/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0=\ngithub.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=\ngithub.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=\ngithub.com/hazelcast/hazelcast-go-client v0.0.0-20190530123621-6cf767c2f31a/go.mod h1:VhwtcZ7sg3xq7REqGzEy7ylSWGKz4jZd05eCJropNzI=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/huaweicloud/huaweicloud-sdk-go-obs v3.21.12+incompatible/go.mod h1:l7VUhRbTKCzdOacdT4oWCwATKyvZqUOlOqr0Ous3k4s=\ngithub.com/huaweicloud/huaweicloud-sdk-go-v3 v0.0.87/go.mod h1:IvF+Pe06JMUivVgN6B4wcsPEoFvVa40IYaOPZyUt5HE=\ngithub.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=\ngithub.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=\ngithub.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=\ngithub.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=\ngithub.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=\ngithub.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=\ngithub.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=\ngithub.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/influxdata/influxdb-client-go v1.4.0/go.mod h1:S+oZsPivqbcP1S9ur+T+QqXvrYS3NCZeMQtBoH4D1dw=\ngithub.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=\ngithub.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=\ngithub.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=\ngithub.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ=\ngithub.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=\ngithub.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw=\ngithub.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=\ngithub.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=\ngithub.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=\ngithub.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=\ngithub.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=\ngithub.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=\ngithub.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=\ngithub.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=\ngithub.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=\ngithub.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=\ngithub.com/jackc/pgconn v1.10.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=\ngithub.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=\ngithub.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=\ngithub.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=\ngithub.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=\ngithub.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=\ngithub.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=\ngithub.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=\ngithub.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=\ngithub.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=\ngithub.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=\ngithub.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=\ngithub.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=\ngithub.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=\ngithub.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=\ngithub.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=\ngithub.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=\ngithub.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=\ngithub.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=\ngithub.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=\ngithub.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=\ngithub.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=\ngithub.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=\ngithub.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=\ngithub.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=\ngithub.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=\ngithub.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=\ngithub.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=\ngithub.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=\ngithub.com/jackc/pgtype v1.8.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=\ngithub.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=\ngithub.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw=\ngithub.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=\ngithub.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=\ngithub.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=\ngithub.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=\ngithub.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=\ngithub.com/jackc/pgx/v4 v4.13.0/go.mod h1:9P4X524sErlaxj0XSGZk7s+LD0eOyu1ZDUrrpznYDF0=\ngithub.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw=\ngithub.com/jackc/pgx/v4 v4.18.2 h1:xVpYkNR5pk5bMCZGfClbO962UIqVABcAGt7ha1s/FeU=\ngithub.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=\ngithub.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=\ngithub.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=\ngithub.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=\ngithub.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=\ngithub.com/jawher/mow.cli v1.0.4/go.mod h1:5hQj2V8g+qYmLUVWqu4Wuja1pI57M83EChYLVZ0sMKk=\ngithub.com/jawher/mow.cli v1.2.0/go.mod h1:y+pcA3jBAdo/GIZx/0rFjw/K2bVEODP9rfZOfaiq8Ko=\ngithub.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=\ngithub.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=\ngithub.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=\ngithub.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=\ngithub.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=\ngithub.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc=\ngithub.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=\ngithub.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=\ngithub.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=\ngithub.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI=\ngithub.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI=\ngithub.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ=\ngithub.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=\ngithub.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E=\ngithub.com/jhump/protoreflect v1.12.0/go.mod h1:JytZfP5d0r8pVNLZvai7U/MCuTWITgrI4tTg7puQFKI=\ngithub.com/jhump/protoreflect v1.14.1/go.mod h1:JytZfP5d0r8pVNLZvai7U/MCuTWITgrI4tTg7puQFKI=\ngithub.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=\ngithub.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=\ngithub.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=\ngithub.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=\ngithub.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=\ngithub.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=\ngithub.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=\ngithub.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=\ngithub.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=\ngithub.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=\ngithub.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=\ngithub.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=\ngithub.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=\ngithub.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=\ngithub.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=\ngithub.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=\ngithub.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=\ngithub.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=\ngithub.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=\ngithub.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=\ngithub.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=\ngithub.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=\ngithub.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=\ngithub.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=\ngithub.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=\ngithub.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=\ngithub.com/kataras/go-errors v0.0.3/go.mod h1:K3ncz8UzwI3bpuksXt5tQLmrRlgxfv+52ARvAu1+I+o=\ngithub.com/kataras/go-serializer v0.0.4/go.mod h1:/EyLBhXKQOJ12dZwpUZZje3lGy+3wnvG7QKaVJtm/no=\ngithub.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=\ngithub.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=\ngithub.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=\ngithub.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=\ngithub.com/klauspost/compress v1.10.8/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=\ngithub.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=\ngithub.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=\ngithub.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=\ngithub.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=\ngithub.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=\ngithub.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=\ngithub.com/klauspost/compress v1.15.14 h1:i7WCKDToww0wA+9qrUZ1xOjp218vfFo3nTU6UHp+gOc=\ngithub.com/klauspost/compress v1.15.14/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=\ngithub.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=\ngithub.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=\ngithub.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=\ngithub.com/knadh/koanf v1.4.1/go.mod h1:1cfH5223ZeZUOs8FU2UdTmaNfHpqgtjV0+NHjRO43gs=\ngithub.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7/go.mod h1:Y2SaZf2Rzd0pXkLVhLlCiAXFCLSXAIbTKDivVgff/AM=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=\ngithub.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=\ngithub.com/labd/commercetools-go-sdk v0.3.2/go.mod h1:I+KKNALlg6PcSertsVA7E442koO99GT7gldWqwZlUGo=\ngithub.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=\ngithub.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=\ngithub.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=\ngithub.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=\ngithub.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=\ngithub.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570/go.mod h1:BLt8L9ld7wVsvEWQbuLrUZnCMnUmLZ+CGDzKtclrTlE=\ngithub.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f/go.mod h1:UGmTpUd3rjbtfIpwAPrcfmGf/Z1HS95TATB+m57TPB8=\ngithub.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042/go.mod h1:TPpsiPUEh0zFL1Snz4crhMlBe60PYxRHr5oFF3rRYg0=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=\ngithub.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=\ngithub.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=\ngithub.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=\ngithub.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=\ngithub.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=\ngithub.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=\ngithub.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=\ngithub.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=\ngithub.com/linkedin/goavro/v2 v2.9.8/go.mod h1:UgQUb2N/pmueQYH9bfqFioWxzYCZXSfF8Jw03O5sjqA=\ngithub.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=\ngithub.com/lithammer/shortuuid/v3 v3.0.7 h1:trX0KTHy4Pbwo/6ia8fscyHoGA+mf1jWbPJVuvyJQQ8=\ngithub.com/lithammer/shortuuid/v3 v3.0.7/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaKlRuDIi8tWWmAts=\ngithub.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=\ngithub.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=\ngithub.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=\ngithub.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=\ngithub.com/machinebox/graphql v0.2.2/go.mod h1:F+kbVMHuwrQ5tYgU9JXlnskM8nOaFxCAEolaQybkjWA=\ngithub.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=\ngithub.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=\ngithub.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=\ngithub.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=\ngithub.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=\ngithub.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=\ngithub.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=\ngithub.com/matoous/go-nanoid v1.5.0/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U=\ngithub.com/matoous/go-nanoid/v2 v2.0.0/go.mod h1:FtS4aGPVfEkxKxhdWPAspZpZSh1cOjtM7Ej/So3hR0g=\ngithub.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=\ngithub.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=\ngithub.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=\ngithub.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=\ngithub.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=\ngithub.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=\ngithub.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=\ngithub.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=\ngithub.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=\ngithub.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=\ngithub.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=\ngithub.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=\ngithub.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=\ngithub.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=\ngithub.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=\ngithub.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=\ngithub.com/microcosm-cc/bluemonday v1.0.7/go.mod h1:HOT/6NaBlR0f9XlxD3zolN6Z3N8Lp4pvhp+jLS5ihnI=\ngithub.com/microsoft/ApplicationInsights-Go v0.4.4/go.mod h1:fKRUseBqkw6bDiXTs3ESTiU/4YTIHsQS4W3fP2ieF4U=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=\ngithub.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=\ngithub.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=\ngithub.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=\ngithub.com/miekg/dns v1.1.45 h1:g5fRIhm9nx7g8osrAvgb16QJfmyMsyOCb+J7LSv+Qzk=\ngithub.com/miekg/dns v1.1.45/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=\ngithub.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=\ngithub.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=\ngithub.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=\ngithub.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=\ngithub.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=\ngithub.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/go-testing-interface v1.14.0/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=\ngithub.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 h1:BpfhmLKZf+SjVanKKhCgf3bg+511DmU9eDQTen7LLbY=\ngithub.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=\ngithub.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=\ngithub.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=\ngithub.com/mkevac/debugcharts v0.0.0-20191222103121-ae1c48aa8615/go.mod h1:Ad7oeElCZqA1Ufj0U9/liOF4BtVepxRcTvr2ey7zTvM=\ngithub.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=\ngithub.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=\ngithub.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM=\ngithub.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=\ngithub.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=\ngithub.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=\ngithub.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg=\ngithub.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ=\ngithub.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs=\ngithub.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=\ngithub.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=\ngithub.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=\ngithub.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=\ngithub.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=\ngithub.com/montanaflynn/stats v0.6.6 h1:Duep6KMIDpY4Yo11iFsvyqJDyfzLF9+sndUKT+v64GQ=\ngithub.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=\ngithub.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=\ngithub.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=\ngithub.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=\ngithub.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=\ngithub.com/mrz1836/postmark v1.2.9/go.mod h1:xNRms8jgTfqBneqg0+PzvBrhuojefqXIWc6Np0nHiEM=\ngithub.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=\ngithub.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=\ngithub.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=\ngithub.com/nacos-group/nacos-sdk-go v1.0.8/go.mod h1:hlAPn3UdzlxIlSILAyOXKxjFSvDJ9oLzTJ9hLAK1KzA=\ngithub.com/nacos-group/nacos-sdk-go v1.0.9/go.mod h1:hlAPn3UdzlxIlSILAyOXKxjFSvDJ9oLzTJ9hLAK1KzA=\ngithub.com/nacos-group/nacos-sdk-go v1.1.1 h1:beczWcOoTaVBMgCgikqvZflrN5Xbw7pWAWpxl+VJGIA=\ngithub.com/nacos-group/nacos-sdk-go v1.1.1/go.mod h1:UHOtQNQY/qpk2dhg6gDq8u5+/CEIc3+lWmrmxEzX0/g=\ngithub.com/nacos-group/nacos-sdk-go/v2 v2.0.1/go.mod h1:SlhyCAv961LcZ198XpKfPEQqlJWt2HkL1fDLas0uy/w=\ngithub.com/nacos-group/nacos-sdk-go/v2 v2.1.0 h1:PxRwOzHhnK6eGGvioEGkn8s6XRXmUVuXu91i2yQcdDs=\ngithub.com/nacos-group/nacos-sdk-go/v2 v2.1.0/go.mod h1:ys/1adWeKXXzbNWfRNbaFlX/t6HVLWdpsNDvmoWTw0g=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=\ngithub.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=\ngithub.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=\ngithub.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=\ngithub.com/nats-io/jwt v1.1.0/go.mod h1:n3cvmLfBfnpV4JJRN7lRYCyZnw48ksGsbThGXEk4w9M=\ngithub.com/nats-io/jwt/v2 v2.2.1-0.20220113022732-58e87895b296/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k=\ngithub.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=\ngithub.com/nats-io/nats-server/v2 v2.1.9/go.mod h1:9qVyoewoYXzG1ME9ox0HwkkzyYvnlBDugfR4Gg/8uHU=\ngithub.com/nats-io/nats-server/v2 v2.7.4/go.mod h1:1vZ2Nijh8tcyNe8BDVyTviCd9NYzRbubQYiEHsvOQWc=\ngithub.com/nats-io/nats-streaming-server v0.21.2/go.mod h1:2W8QfNVOtcFpmf0bRiwuLtRb0/hkX4NuOxPOFNOThVQ=\ngithub.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=\ngithub.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE=\ngithub.com/nats-io/nats.go v1.13.1-0.20220308171302-2f2f6968e98d/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=\ngithub.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=\ngithub.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=\ngithub.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s=\ngithub.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=\ngithub.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=\ngithub.com/nats-io/stan.go v0.8.3/go.mod h1:Ejm8bbHnMTSptU6uNMAVuxeapMJYBB/Ml3ej6z4GoSY=\ngithub.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=\ngithub.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=\ngithub.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=\ngithub.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=\ngithub.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=\ngithub.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=\ngithub.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=\ngithub.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=\ngithub.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=\ngithub.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=\ngithub.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=\ngithub.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs=\ngithub.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=\ngithub.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=\ngithub.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=\ngithub.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=\ngithub.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=\ngithub.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=\ngithub.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=\ngithub.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=\ngithub.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=\ngithub.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys=\ngithub.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=\ngithub.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=\ngithub.com/open-policy-agent/opa v0.40.0/go.mod h1:UQqv8nJ1njs2+Od1lrPFzUAApdj22ABxTO35+Vpsjz4=\ngithub.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=\ngithub.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=\ngithub.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=\ngithub.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=\ngithub.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=\ngithub.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=\ngithub.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=\ngithub.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=\ngithub.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=\ngithub.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=\ngithub.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=\ngithub.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=\ngithub.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=\ngithub.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=\ngithub.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=\ngithub.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=\ngithub.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=\ngithub.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=\ngithub.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=\ngithub.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=\ngithub.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=\ngithub.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=\ngithub.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=\ngithub.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=\ngithub.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=\ngithub.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=\ngithub.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=\ngithub.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=\ngithub.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=\ngithub.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=\ngithub.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=\ngithub.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=\ngithub.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=\ngithub.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=\ngithub.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=\ngithub.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=\ngithub.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=\ngithub.com/openzipkin/zipkin-go v0.3.0/go.mod h1:4c3sLeE8xjNqehmF5RpAFLPLJxXscc0R4l6Zg0P1tTQ=\ngithub.com/openzipkin/zipkin-go v0.4.0/go.mod h1:4c3sLeE8xjNqehmF5RpAFLPLJxXscc0R4l6Zg0P1tTQ=\ngithub.com/oracle/oci-go-sdk/v54 v54.0.0/go.mod h1:+t+yvcFGVp+3ZnztnyxqXfQDsMlq8U25faBLa+mqCMc=\ngithub.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=\ngithub.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=\ngithub.com/paulmach/orb v0.5.0/go.mod h1:FWRlTgl88VI1RBx/MkrwWDRhQ96ctqMCh8boXhmqB/A=\ngithub.com/paulmach/orb v0.7.1/go.mod h1:FWRlTgl88VI1RBx/MkrwWDRhQ96ctqMCh8boXhmqB/A=\ngithub.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=\ngithub.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=\ngithub.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=\ngithub.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=\ngithub.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=\ngithub.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI=\ngithub.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=\ngithub.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=\ngithub.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=\ngithub.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=\ngithub.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=\ngithub.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=\ngithub.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=\ngithub.com/pierrec/lz4 v0.0.0-20190327172049-315a67e90e41/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=\ngithub.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=\ngithub.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=\ngithub.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=\ngithub.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=\ngithub.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=\ngithub.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=\ngithub.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=\ngithub.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=\ngithub.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=\ngithub.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/polarismesh/grpc-go-polaris v1.2.1-0.20220306155244-f0b83ba62878 h1:IhlY8X1AZT33oB920PES1L/SPURx+tjb62nWHZ7s0Ss=\ngithub.com/polarismesh/grpc-go-polaris v1.2.1-0.20220306155244-f0b83ba62878/go.mod h1:DxKBmYOXsLNqbrMqJgwnGwu9RkqWl005kXosGaVxbTg=\ngithub.com/polarismesh/polaris-go v1.0.1/go.mod h1:3NOqn3QquPdEdY6YhPrsWGvBVCpKhPBGt0Hspq3yEqY=\ngithub.com/polarismesh/polaris-go v1.1.0 h1:nFvn3q3XaVFhzF7pBnIySrN0ZZBwvbbYXC5r2DpsQN0=\ngithub.com/polarismesh/polaris-go v1.1.0/go.mod h1:tquawfjEKp1W3ffNJQSzhfditjjoZ7tvhOCElN7Efzs=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=\ngithub.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=\ngithub.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=\ngithub.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=\ngithub.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=\ngithub.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=\ngithub.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=\ngithub.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=\ngithub.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=\ngithub.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=\ngithub.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=\ngithub.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=\ngithub.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=\ngithub.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU=\ngithub.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=\ngithub.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=\ngithub.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=\ngithub.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=\ngithub.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=\ngithub.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=\ngithub.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=\ngithub.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=\ngithub.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=\ngithub.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=\ngithub.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=\ngithub.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=\ngithub.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=\ngithub.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=\ngithub.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=\ngithub.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=\ngithub.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=\ngithub.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=\ngithub.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE=\ngithub.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=\ngithub.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=\ngithub.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=\ngithub.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=\ngithub.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=\ngithub.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=\ngithub.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=\ngithub.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=\ngithub.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=\ngithub.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=\ngithub.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=\ngithub.com/prometheus/statsd_exporter v0.21.0/go.mod h1:rbT83sZq2V+p73lHhPZfMc3MLCHmSHelCh9hSGYNLTQ=\ngithub.com/prometheus/statsd_exporter v0.22.3/go.mod h1:N4Z1+iSqc9rnxlT1N8Qn3l65Vzb5t4Uq0jpg8nxyhio=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/rabbitmq/amqp091-go v1.1.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM=\ngithub.com/rabbitmq/amqp091-go v1.3.4/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM=\ngithub.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=\ngithub.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=\ngithub.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=\ngithub.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=\ngithub.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=\ngithub.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=\ngithub.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=\ngithub.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=\ngithub.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=\ngithub.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=\ngithub.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=\ngithub.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=\ngithub.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=\ngithub.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=\ngithub.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=\ngithub.com/rs/zerolog v1.18.0/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF5+I=\ngithub.com/rs/zerolog v1.25.0/go.mod h1:7KHcEGe0QZPOm2IE4Kpb5rTh6n1h2hIgS5OOnu1rUaI=\ngithub.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=\ngithub.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=\ngithub.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=\ngithub.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=\ngithub.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=\ngithub.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=\ngithub.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=\ngithub.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=\ngithub.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=\ngithub.com/savsgio/gotils v0.0.0-20210217112953-d4a072536008/go.mod h1:TWNAOTaVzGOXq8RbEvHnhzA/A2sLZzgn0m6URjnukY8=\ngithub.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=\ngithub.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=\ngithub.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=\ngithub.com/sendgrid/rest v2.6.3+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=\ngithub.com/sendgrid/sendgrid-go v3.5.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=\ngithub.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=\ngithub.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=\ngithub.com/shirou/gopsutil v3.20.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=\ngithub.com/shirou/gopsutil v3.21.3+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=\ngithub.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=\ngithub.com/shirou/gopsutil/v3 v3.21.6/go.mod h1:JfVbDpIBLVzT8oKbvMg9P3wEIMDDpVn+LwHTKj0ST88=\ngithub.com/shirou/gopsutil/v3 v3.21.8/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ=\ngithub.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=\ngithub.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=\ngithub.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=\ngithub.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=\ngithub.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/sijms/go-ora/v2 v2.2.22/go.mod h1:jzfAFD+4CXHE+LjGWFl6cPrtiIpQVxakI2gvrMF2w6Y=\ngithub.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=\ngithub.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=\ngithub.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=\ngithub.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=\ngithub.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0=\ngithub.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=\ngithub.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/soheilhy/cmux v0.1.5-0.20210205191134-5ec6847320e5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=\ngithub.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=\ngithub.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=\ngithub.com/sony/gobreaker v0.4.2-0.20210216022020-dd874f9dd33b/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=\ngithub.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=\ngithub.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=\ngithub.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=\ngithub.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=\ngithub.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=\ngithub.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=\ngithub.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=\ngithub.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=\ngithub.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=\ngithub.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=\ngithub.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=\ngithub.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=\ngithub.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=\ngithub.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=\ngithub.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=\ngithub.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=\ngithub.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=\ngithub.com/stathat/consistent v1.0.0/go.mod h1:uajTPbgSygZBJ+V+0mY7meZ8i0XAcZs7AQ6V121XSxw=\ngithub.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=\ngithub.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=\ngithub.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=\ngithub.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=\ngithub.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=\ngithub.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=\ngithub.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=\ngithub.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=\ngithub.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/supplyon/gremcos v0.1.0/go.mod h1:ZnXsXGVbGCYDFU5GLPX9HZLWfD+ZWkiPo30KUjNoOtw=\ngithub.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=\ngithub.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=\ngithub.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=\ngithub.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=\ngithub.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ=\ngithub.com/tedsuo/ifrit v0.0.0-20180802180643-bea94bb476cc/go.mod h1:eyZnKCc955uh98WQvzOm0dgAeLnf2O0Rz0LPoC5ze+0=\ngithub.com/testcontainers/testcontainers-go v0.12.0/go.mod h1:SIndOQXZng0IW8iWU1Js0ynrfZ8xcxrTtDfF6rD2pxs=\ngithub.com/tetratelabs/wazero v0.0.0-20220425003459-ad61d9a6ff43/go.mod h1:Y4X/zO4sC2dJjZG9GDYNRbJGogfqFYJY/BbyKlOxXGI=\ngithub.com/tevid/gohamcrest v1.1.1/go.mod h1:3UvtWlqm8j5JbwYZh80D/PVBt0mJ1eJiYgZMibh0H/k=\ngithub.com/tidwall/gjson v1.2.1/go.mod h1:c/nTNbUr0E0OrXEhq1pwa8iEgc2DOt4ZZqAt1HtCkPA=\ngithub.com/tidwall/gjson v1.8.1/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=\ngithub.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=\ngithub.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=\ngithub.com/tidwall/pretty v0.0.0-20190325153808-1166b9ac2b65/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=\ngithub.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=\ngithub.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=\ngithub.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=\ngithub.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=\ngithub.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=\ngithub.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=\ngithub.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=\ngithub.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=\ngithub.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=\ngithub.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=\ngithub.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3/go.mod h1:QDlpd3qS71vYtakd2hmdpqhJ9nwv6mD6A30bQ1BPBFE=\ngithub.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=\ngithub.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=\ngithub.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=\ngithub.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=\ngithub.com/uber/jaeger-client-go v2.23.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=\ngithub.com/uber/jaeger-client-go v2.29.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=\ngithub.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=\ngithub.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=\ngithub.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngithub.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=\ngithub.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=\ngithub.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=\ngithub.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=\ngithub.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=\ngithub.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=\ngithub.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=\ngithub.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/valyala/fasthttp v1.21.0/go.mod h1:jjraHZVbKOXftJfsOYoAjaeygpj5hr8ermTRJNroD7A=\ngithub.com/valyala/fasthttp v1.31.1-0.20211216042702-258a4c17b4f4/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus=\ngithub.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=\ngithub.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=\ngithub.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=\ngithub.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=\ngithub.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=\ngithub.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=\ngithub.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=\ngithub.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=\ngithub.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=\ngithub.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=\ngithub.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=\ngithub.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=\ngithub.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=\ngithub.com/vmware/vmware-go-kcl v1.5.0/go.mod h1:P92YfaWfQyudNf62BNx+E2rJn9pd165MhHsRt8ajkpM=\ngithub.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=\ngithub.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=\ngithub.com/wk8/go-ordered-map v0.2.0/go.mod h1:9ZIbRunKbuvfPKyBP1SIKLcXNlv74YCOZ3t3VTS6gRk=\ngithub.com/wk8/go-ordered-map v1.0.0/go.mod h1:9ZIbRunKbuvfPKyBP1SIKLcXNlv74YCOZ3t3VTS6gRk=\ngithub.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=\ngithub.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=\ngithub.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=\ngithub.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=\ngithub.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=\ngithub.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=\ngithub.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=\ngithub.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=\ngithub.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=\ngithub.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=\ngithub.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=\ngithub.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngithub.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=\ngithub.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yashtewari/glob-intersection v0.1.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok=\ngithub.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=\ngithub.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngithub.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=\ngithub.com/yuin/gopher-lua v0.0.0-20200603152657-dc2b0ca8b37e/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=\ngithub.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=\ngithub.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=\ngithub.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=\ngithub.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=\ngithub.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=\ngithub.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=\ngithub.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=\ngithub.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=\ngithub.com/zeromicro/go-zero v1.3.0/go.mod h1:Hy4o1VFAt32lXaQMbaBhoFeZjA/rJqJ4PTGNdGsURcc=\ngithub.com/zeromicro/go-zero v1.3.2/go.mod h1:DEj3Fwj1Ui1ltsgf6YqwTL9nD4+tYzIRX0c1pWtQo1E=\ngithub.com/zeromicro/go-zero v1.3.5/go.mod h1:wh4o794b7Ul3W0k35Pw9nc3iB4O0OpaQTMQz/PJc1bc=\ngithub.com/zeromicro/go-zero v1.4.4 h1:J8M768EVFNtIQJ/GCEsoIQPanxbx2HHT0it7r69U76Y=\ngithub.com/zeromicro/go-zero v1.4.4/go.mod h1:5WSUwtJm0bYdDZ69GlckigcT6D0EyAPbDaX3unbSY/4=\ngithub.com/zeromicro/zero-contrib/zrpc/registry/consul v0.0.0-20220228111653-d672d81f39ab/go.mod h1:kM7gqMjv0B7QI1UsmJhN5UVWxQao9Oe3Jt3JnXc9o+c=\ngithub.com/zeromicro/zero-contrib/zrpc/registry/consul v0.0.0-20230212061721-86dbe4a9e613 h1:Tom7SdJi3QSPRomxU8hBqxdXULFWKnIreAy8mDE/hOw=\ngithub.com/zeromicro/zero-contrib/zrpc/registry/consul v0.0.0-20230212061721-86dbe4a9e613/go.mod h1:CGD4RBJnrXLk2hMIhQ36WX0TWy3q7+o16XClCDppyS0=\ngithub.com/zeromicro/zero-contrib/zrpc/registry/nacos v0.0.0-20220525162615-f10f16d580d6 h1:2+DgXySLxSsB0LoYgk+AinZKnWq2mc5lxbqUQ7qTcvY=\ngithub.com/zeromicro/zero-contrib/zrpc/registry/nacos v0.0.0-20220525162615-f10f16d580d6/go.mod h1:5yKg0EdtswP7sn1snNAkZ/6aj/zBYcyWOqTpLSAtW3M=\ngithub.com/zhufuyi/dtmdriver-sponge v0.0.2 h1:YCXEGtO+9ThVzOlbbcOUPai69CfBUcNm6KuYFGDySec=\ngithub.com/zhufuyi/dtmdriver-sponge v0.0.2/go.mod h1:IIXUNeJis54f2CL5CNJqIpqRAdRKNpQdsA1LQMYv+vo=\ngithub.com/zouyx/agollo/v3 v3.4.5/go.mod h1:LJr3kDmm23QSW+F1Ol4TMHDa7HvJvscMdVxJ2IpUTVc=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=\ngo.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=\ngo.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=\ngo.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=\ngo.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=\ngo.etcd.io/etcd/api/v3 v3.5.0-alpha.0/go.mod h1:mPcW6aZJukV6Aa81LSKpBjQXTWlXB5r74ymPoSWa3Sw=\ngo.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=\ngo.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=\ngo.etcd.io/etcd/api/v3 v3.5.2/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=\ngo.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=\ngo.etcd.io/etcd/api/v3 v3.5.5 h1:BX4JIbQ7hl7+jL+g+2j5UAr0o1bctCm6/Ct+ArBGkf0=\ngo.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.2/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.5 h1:9S0JUVvmrVl7wCF39iTQthdaaNIiAaQbmK75ogO6GU8=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.5/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ=\ngo.etcd.io/etcd/client/v2 v2.305.0-alpha.0/go.mod h1:kdV+xzCJ3luEBSIeQyB/OEKkWKd8Zkux4sbDeANrosU=\ngo.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=\ngo.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=\ngo.etcd.io/etcd/client/v3 v3.5.0-alpha.0/go.mod h1:wKt7jgDgf/OfKiYmCq5WFGxOFAkVMLxiiXgLDFhECr8=\ngo.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=\ngo.etcd.io/etcd/client/v3 v3.5.1/go.mod h1:OnjH4M8OnAotwaB2l9bVgZzRFKru7/ZMoS46OtKyd3Q=\ngo.etcd.io/etcd/client/v3 v3.5.2/go.mod h1:kOOaWFFgHygyT0WlSmL8TJiXmMysO/nNUlEsSsN6W4o=\ngo.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=\ngo.etcd.io/etcd/client/v3 v3.5.5 h1:q++2WTJbUgpQu4B6hCuT7VkdwaTP7Qz6Daak3WzbrlI=\ngo.etcd.io/etcd/client/v3 v3.5.5/go.mod h1:aApjR4WGlSumpnJ2kloS75h6aHUmAyaPLjHMxpc7E7c=\ngo.etcd.io/etcd/pkg/v3 v3.5.0-alpha.0/go.mod h1:tV31atvwzcybuqejDoY3oaNRTtlD2l/Ot78Pc9w7DMY=\ngo.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE=\ngo.etcd.io/etcd/raft/v3 v3.5.0-alpha.0/go.mod h1:FAwse6Zlm5v4tEWZaTjmNhe17Int4Oxbu7+2r0DiD3w=\ngo.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc=\ngo.etcd.io/etcd/server/v3 v3.5.0-alpha.0/go.mod h1:tsKetYpt980ZTpzl/gb+UOJj9RkIyCb1u4wjzMg90BQ=\ngo.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4=\ngo.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw=\ngo.mongodb.org/mongo-driver v1.9.1/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=\ngo.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=\ngo.mongodb.org/mongo-driver v1.11.6 h1:XM7G6PjiGAO5betLF13BIa5TlLUUE3uJ/2Ox3Lz1K+o=\ngo.mongodb.org/mongo-driver v1.11.6/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY=\ngo.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=\ngo.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=\ngo.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=\ngo.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=\ngo.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=\ngo.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.31.0/go.mod h1:PFmBsWbldL1kiWZk9+0LBZz2brhByaGsvp6pRICMlPE=\ngo.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=\ngo.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I=\ngo.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=\ngo.opentelemetry.io/otel v1.6.0/go.mod h1:bfJD2DZVw0LBxghOTlgnlI0CV3hLDu9XF/QKOUXMTQQ=\ngo.opentelemetry.io/otel v1.6.1/go.mod h1:blzUabWHkX6LJewxvadmzafgh/wnvBSDBdOuwkAtrWQ=\ngo.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI=\ngo.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=\ngo.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM=\ngo.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ=\ngo.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=\ngo.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=\ngo.opentelemetry.io/otel/exporters/jaeger v1.2.0/go.mod h1:KJLFbEMKTNPIfOxcg/WikIozEoKcPgJRz3Ce1vLlM8E=\ngo.opentelemetry.io/otel/exporters/jaeger v1.3.0/go.mod h1:KoYHi1BtkUPncGSRtCe/eh1ijsnePhSkxwzz07vU0Fc=\ngo.opentelemetry.io/otel/exporters/jaeger v1.7.0/go.mod h1:PwQAOqBgqbLQRKlj466DuD2qyMjbtcPpfPfj+AqbSBs=\ngo.opentelemetry.io/otel/exporters/jaeger v1.8.0/go.mod h1:GbWg+ng88rDtx+id26C34QLqw2erqJeAjsCx9AFeHfE=\ngo.opentelemetry.io/otel/exporters/jaeger v1.10.0/go.mod h1:n9IGyx0fgyXXZ/i0foLHNxtET9CzXHzZeKCucvRBFgA=\ngo.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.3/go.mod h1:NEu79Xo32iVb+0gVNV8PMd7GoWqnyDXRlj04yFjqz40=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0/go.mod h1:M1hVZHNxcbkAlcvrOMlpQ4YOO3Awf+4N2dxkZL3xm04=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0/go.mod h1:78XhIg8Ht9vR4tbLNUhXsiOnE2HOuSeKAiAcoVQEpOY=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.6.3/go.mod h1:UJmXdiVVBaZ63umRUTwJuCMAV//GCMvDiQwn703/GoY=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0/go.mod h1:ceUgdyfNv4h4gLxHR0WNfDiiVmZFodZhZSbOLhpxqXE=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0/go.mod h1:Krqnjl22jUJ0HgMzw5eveuCvFDXY4nSYb4F8t5gdrag=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.6.3/go.mod h1:ycItY/esVj8c0dKgYTOztTERXtPzcfDU/0o8EdwCjoA=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0/go.mod h1:E+/KKhwOSw8yoPxSSuUHG6vKppkvhN+S1Jc7Nib3k3o=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.10.0/go.mod h1:OfUCyyIiDvNXHWpcWgbF+MWvqPZiNa3YDEnivcnYsV0=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.10.0/go.mod h1:5WV40MLWwvWlGP7Xm8g3pMcg0pKOUY609qxJn8y7LmM=\ngo.opentelemetry.io/otel/exporters/zipkin v1.3.0/go.mod h1:LxGGfHIYbvsFnrJtBcazb0yG24xHdDGrT/H6RB9r3+8=\ngo.opentelemetry.io/otel/exporters/zipkin v1.8.0/go.mod h1:0uYAyCuGT67MFV9Z/Mmx93wGuugHw0FbxMc74fs3LNo=\ngo.opentelemetry.io/otel/exporters/zipkin v1.10.0/go.mod h1:HdfvgwcOoCB0+zzrTHycW6btjK0zNpkz2oTGO815SCI=\ngo.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=\ngo.opentelemetry.io/otel/metric v0.28.0/go.mod h1:TrzsfQAmQaB1PDcdhBauLMk7nyyg9hm+GoQq/ekE9Iw=\ngo.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=\ngo.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=\ngo.opentelemetry.io/otel/sdk v1.2.0/go.mod h1:jNN8QtpvbsKhgaC6V5lHiejMoKD+V8uadoSafgHPx1U=\ngo.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=\ngo.opentelemetry.io/otel/sdk v1.6.3/go.mod h1:A4iWF7HTXa+GWL/AaqESz28VuSBIcZ+0CV+IzJ5NMiQ=\ngo.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU=\ngo.opentelemetry.io/otel/sdk v1.8.0/go.mod h1:uPSfc+yfDH2StDM/Rm35WE8gXSNdvCg023J6HeGNO0c=\ngo.opentelemetry.io/otel/sdk v1.10.0/go.mod h1:vO06iKzD5baltJz1zarxMCNHFpUlUiOy4s65ECtn6kE=\ngo.opentelemetry.io/otel/sdk v1.11.2 h1:GF4JoaEx7iihdMFu30sOyRx52HDHOkl9xQ8SMqNXUiU=\ngo.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE=\ngo.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE=\ngo.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=\ngo.opentelemetry.io/otel/trace v1.2.0/go.mod h1:N5FLswTubnxKxOJHM7XZC074qpeEdLy3CgAVsdMucK0=\ngo.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=\ngo.opentelemetry.io/otel/trace v1.6.0/go.mod h1:qs7BrU5cZ8dXQHBGxHMOxwME/27YH2qEp4/+tZLLwJE=\ngo.opentelemetry.io/otel/trace v1.6.1/go.mod h1:RkFRM1m0puWIq10oxImnGEduNBzxiN7TXluRBtE+5j0=\ngo.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs=\ngo.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=\ngo.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaTBoTCh2zIFI4=\ngo.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM=\ngo.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=\ngo.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ=\ngo.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=\ngo.opentelemetry.io/proto/otlp v0.16.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=\ngo.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=\ngo.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=\ngo.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=\ngo.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=\ngo.uber.org/automaxprocs v1.3.0/go.mod h1:9CWT6lKIep8U41DDaPiH6eFscnTyjfTANNQNx6LrIcA=\ngo.uber.org/automaxprocs v1.4.0/go.mod h1:/mTEdr7LvHhs0v7mjdxDreTz1OG5zdZGqgOnhWiR/+Q=\ngo.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk=\ngo.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU=\ngo.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=\ngo.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=\ngo.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=\ngo.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=\ngo.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=\ngo.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=\ngo.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=\ngo.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngo.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=\ngo.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=\ngo.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=\ngo.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=\ngo.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=\ngo.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=\ngo.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=\ngoji.io v2.0.2+incompatible/go.mod h1:sbqFwrtqZACxLBTQcdgVjFh54yGVCvwq8+w49MVMMIk=\ngolang.org/x/arch v0.0.0-20180920145803-b19384d3c130/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8=\ngolang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=\ngolang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=\ngolang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=\ngolang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=\ngolang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=\ngolang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=\ngolang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=\ngolang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=\ngolang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=\ngolang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=\ngolang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=\ngolang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=\ngolang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=\ngolang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg=\ngolang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=\ngolang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=\ngolang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=\ngolang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=\ngolang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=\ngolang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=\ngolang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211105192438-b53810dc28af/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211108170745-6635138e15ea/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220107192237-5cfca573fb4d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220114011407-0dd24b26b47d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.0.0-20220621193019-9d032be2e588/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=\ngolang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=\ngolang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=\ngolang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=\ngolang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=\ngolang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=\ngolang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=\ngolang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=\ngolang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=\ngolang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=\ngolang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=\ngolang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=\ngolang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=\ngolang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=\ngolang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=\ngolang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=\ngolang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=\ngolang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=\ngolang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180828065106-d99a578cf41b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191220220014-0732a990476f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211106132015-ebca88c72f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211109184856-51b60fd695b3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=\ngolang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=\ngolang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=\ngolang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=\ngolang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=\ngolang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=\ngolang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=\ngolang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=\ngolang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=\ngolang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190424220101-1e8e1cfdf96b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=\ngolang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=\ngolang.org/x/tools v0.0.0-20201014170642-d1624618ad65/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=\ngolang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=\ngolang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM=\ngolang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=\ngolang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=\ngolang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=\ngolang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=\ngolang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=\ngolang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=\ngomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY=\ngonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=\ngonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=\ngonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=\ngonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=\ngoogle.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=\ngoogle.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=\ngoogle.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=\ngoogle.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=\ngoogle.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=\ngoogle.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=\ngoogle.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=\ngoogle.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=\ngoogle.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=\ngoogle.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=\ngoogle.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=\ngoogle.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=\ngoogle.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=\ngoogle.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=\ngoogle.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=\ngoogle.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU=\ngoogle.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=\ngoogle.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw=\ngoogle.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=\ngoogle.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=\ngoogle.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=\ngoogle.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=\ngoogle.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=\ngoogle.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=\ngoogle.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=\ngoogle.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=\ngoogle.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=\ngoogle.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=\ngoogle.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g=\ngoogle.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=\ngoogle.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=\ngoogle.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI=\ngoogle.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=\ngoogle.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=\ngoogle.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=\ngoogle.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08=\ngoogle.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70=\ngoogle.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo=\ngoogle.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=\ngoogle.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=\ngoogle.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=\ngoogle.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201102152239-715cce707fb0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210106152847-07624b53cd92/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=\ngoogle.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=\ngoogle.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=\ngoogle.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=\ngoogle.golang.org/genproto v0.0.0-20210701133433-6b8dcf568a95/go.mod h1:yiaVoXHpRzHGyxV3o4DktVWY4mSUErTKaeEOq6C3t3U=\ngoogle.golang.org/genproto v0.0.0-20210707164411-8c882eb9abba/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=\ngoogle.golang.org/genproto v0.0.0-20210708141623-e76da96a951f/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=\ngoogle.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=\ngoogle.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=\ngoogle.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=\ngoogle.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=\ngoogle.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=\ngoogle.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20211104193956-4c6863e31247/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20220112215332-a9c7c0acf9f2/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=\ngoogle.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=\ngoogle.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=\ngoogle.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=\ngoogle.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=\ngoogle.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=\ngoogle.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=\ngoogle.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=\ngoogle.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=\ngoogle.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=\ngoogle.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=\ngoogle.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=\ngoogle.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=\ngoogle.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=\ngoogle.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=\ngoogle.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=\ngoogle.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=\ngoogle.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8/go.mod h1:yKyY4AMRwFiC8yMMNaMi+RkCnjZJt9LoWuvhXjMs+To=\ngoogle.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=\ngoogle.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=\ngoogle.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=\ngoogle.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=\ngoogle.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=\ngoogle.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=\ngoogle.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE=\ngoogle.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=\ngoogle.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=\ngoogle.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=\ngoogle.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=\ngoogle.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=\ngoogle.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=\ngoogle.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=\ngoogle.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=\ngoogle.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=\ngoogle.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=\ngoogle.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=\ngoogle.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw=\ngoogle.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=\ngoogle.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=\ngoogle.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U=\ngoogle.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=\ngoogle.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=\ngoogle.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s=\ngoogle.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s=\ngoogle.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo=\ngoogle.golang.org/genproto v0.0.0-20221111202108-142d8a6fa32e/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=\ngoogle.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=\ngoogle.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=\ngoogle.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=\ngoogle.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=\ngoogle.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE=\ngoogle.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=\ngoogle.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=\ngoogle.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=\ngoogle.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=\ngoogle.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=\ngoogle.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=\ngoogle.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=\ngoogle.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=\ngoogle.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=\ngoogle.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=\ngoogle.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=\ngoogle.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=\ngoogle.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=\ngoogle.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=\ngoogle.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=\ngoogle.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=\ngoogle.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=\ngoogle.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=\ngoogle.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=\ngoogle.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=\ngoogle.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=\ngoogle.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=\ngoogle.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=\ngoogle.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=\ngoogle.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=\ngoogle.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngoogle.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngoogle.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=\ngoogle.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngoogle.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=\ngoogle.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\ngopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=\ngopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=\ngopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=\ngopkg.in/couchbase/gocb.v1 v1.6.4/go.mod h1:Ri5Qok4ZKiwmPr75YxZ0uELQy45XJgUSzeUnK806gTY=\ngopkg.in/couchbase/gocbcore.v7 v7.1.18/go.mod h1:48d2Be0MxRtsyuvn+mWzqmoGUG9uA00ghopzOs148/E=\ngopkg.in/couchbaselabs/gocbconnstr.v1 v1.0.4/go.mod h1:ZjII0iKx4Veo6N6da+pEZu/ptNyKLg9QTVt7fFmR6sw=\ngopkg.in/couchbaselabs/gojcbmock.v1 v1.0.4/go.mod h1:jl/gd/aQ2S8whKVSTnsPs6n7BPeaAuw9UglBD/OF7eo=\ngopkg.in/couchbaselabs/jsonx.v1 v1.0.1/go.mod h1:oR201IRovxvLW/eISevH12/+MiKHtNQAKfcX8iWZvJY=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fatih/pool.v2 v2.0.0/go.mod h1:8xVGeu1/2jr2wm5V9SPuMht2H5AEmf5aFMGSQixtjTY=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=\ngopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=\ngopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=\ngopkg.in/gorethink/gorethink.v4 v4.1.0/go.mod h1:M7JgwrUAmshJ3iUbEK0Pt049MPyPK+CYDGGaEjdZb/c=\ngopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=\ngopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=\ngopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI=\ngopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo=\ngopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q=\ngopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4=\ngopkg.in/jcmturner/gokrb5.v7 v7.2.3/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM=\ngopkg.in/jcmturner/gokrb5.v7 v7.3.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM=\ngopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8=\ngopkg.in/kataras/go-serializer.v0 v0.0.4/go.mod h1:v2jHg/3Wp7uncDNzenTsX75PRDxhzlxoo/qDvM4ZGxk=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=\ngopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=\ngopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=\ngopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=\ngopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngorm.io/driver/mysql v1.0.3 h1:+JKBYPfn1tygR1/of/Fh2T8iwuVwzt+PEJmKaXzMQXg=\ngorm.io/driver/mysql v1.0.3/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI=\ngorm.io/driver/postgres v1.2.1 h1:JDQKnF7MC51dgL09Vbydc5kl83KkVDlcXfSPJ+xhh68=\ngorm.io/driver/postgres v1.2.1/go.mod h1:SHRZhu+D0tLOHV5qbxZRUM6kBcf3jp/kxPz2mYMTsNY=\ngorm.io/driver/sqlserver v1.1.2 h1:MmOAvxnfqGMYS/I9jMwrMlc1+62S0clG3RUEvfEkTVo=\ngorm.io/driver/sqlserver v1.1.2/go.mod h1:mEmVwRbwsgY+EmU7MWypjefdbX5uFgbE07kklGvLmcg=\ngorm.io/gorm v1.20.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=\ngorm.io/gorm v1.22.0/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=\ngorm.io/gorm v1.22.2 h1:1iKcvyJnR5bHydBhDqTwasOkoo6+o4Ms5cknSt6qP7I=\ngorm.io/gorm v1.22.2/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=\ngotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=\ngotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=\ngotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=\nhonnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nk8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8=\nk8s.io/api v0.20.0/go.mod h1:HyLC5l5eoS/ygQYl1BXBgFzWNlkHiAuyNAbevIn+FKg=\nk8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=\nk8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ=\nk8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8=\nk8s.io/api v0.20.12/go.mod h1:A2brwyEkVLM3wQGNnzoAa5JsQRzHK0uoOQ+bsnv7V68=\nk8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs=\nk8s.io/api v0.22.9/go.mod h1:rcjO/FPOuvc3x7nQWx29UcDrFJMx82RxDob71ntNH4A=\nk8s.io/api v0.23.0/go.mod h1:8wmDdLBHBNxtOIytwLstXt5E9PddnZb0GaMcqsvDBpg=\nk8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ=\nk8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg=\nk8s.io/apiextensions-apiserver v0.23.0/go.mod h1:xIFAEEDlAZgpVBl/1VSjGDmLoXAWRG40+GsWhKhAxY4=\nk8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=\nk8s.io/apimachinery v0.20.0/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=\nk8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=\nk8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=\nk8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc=\nk8s.io/apimachinery v0.20.12/go.mod h1:uM7hCI0NyBymUwgshMgZyte475lxhr+QH6h3cvdnzEc=\nk8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=\nk8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U=\nk8s.io/apimachinery v0.22.9/go.mod h1:ZvVLP5iLhwVFg2Yx9Gh5W0um0DUauExbRhe+2Z8I1EU=\nk8s.io/apimachinery v0.23.0/go.mod h1:fFCTTBKvKcwTPFzjlcxp91uPFZr+JA0FubU4fLzzFYc=\nk8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ=\nk8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=\nk8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=\nk8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM=\nk8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q=\nk8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ=\nk8s.io/apiserver v0.23.0/go.mod h1:Cec35u/9zAepDPPFyT+UMrgqOCjgJ5qtfVJDxjZYmt4=\nk8s.io/cli-runtime v0.23.0/go.mod h1:B5N3YH0KP1iKr6gEuJ/RRmGjO0mJQ/f/JrsmEiPQAlU=\nk8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8=\nk8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY=\nk8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=\nk8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k=\nk8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0=\nk8s.io/client-go v0.20.12/go.mod h1:NBJj6Evp73Xy/4v/O/RDRaH0+3JoxNfjRxkyRgrdbsA=\nk8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y=\nk8s.io/client-go v0.22.9/go.mod h1:IoH7exYnoH/zgvHOuVxh2c4yJepcCBt72FzCTisOc4k=\nk8s.io/client-go v0.23.0/go.mod h1:hrDnpnK1mSr65lHHcUuIZIXDgEbzc7/683c6hyG4jTA=\nk8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU=\nk8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE=\nk8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0=\nk8s.io/code-generator v0.20.0/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg=\nk8s.io/code-generator v0.23.0/go.mod h1:vQvOhDXhuzqiVfM/YHp+dmg10WDZCchJVObc9MvowsE=\nk8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk=\nk8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI=\nk8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM=\nk8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI=\nk8s.io/component-base v0.23.0/go.mod h1:DHH5uiFvLC1edCpvcTDV++NKULdYYU6pR9Tt3HIKMKI=\nk8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM=\nk8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=\nk8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=\nk8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc=\nk8s.io/cri-api v0.23.1/go.mod h1:REJE3PSU0h/LOV1APBrupxrEJqnoxZC8KWzkBUHwrK4=\nk8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=\nk8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=\nk8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=\nk8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=\nk8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=\nk8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=\nk8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=\nk8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=\nk8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=\nk8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=\nk8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=\nk8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=\nk8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=\nk8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=\nk8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4=\nk8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=\nk8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=\nk8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=\nk8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=\nk8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=\nk8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=\nk8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk=\nk8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E=\nk8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4=\nk8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=\nk8s.io/metrics v0.20.0/go.mod h1:9yiRhfr8K8sjdj2EthQQE9WvpYDvsXIV3CjN4Ruq4Jw=\nk8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=\nk8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nk8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nk8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nk8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nk8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nk8s.io/utils v0.0.0-20211208161948-7d6a63dca704/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nk8s.io/utils v0.0.0-20220706174534-f6158b442e7c/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nk8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=\nk8s.io/utils v0.0.0-20230115233650-391b47cb4029 h1:L8zDtT4jrxj+TaQYD0k8KNlr556WaVQylDXswKmX+dE=\nk8s.io/utils v0.0.0-20230115233650-391b47cb4029/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=\nnhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=\nnhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=\noras.land/oras-go v1.1.0/go.mod h1:1A7vR/0KknT2UkJVWh+xMi95I/AhK8ZrxrnUSmXN0bQ=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\nsigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=\nsigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=\nsigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=\nsigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.25/go.mod h1:Mlj9PNLmG9bZ6BHFwFKDo5afkpWyUISkb9Me0GnK66I=\nsigs.k8s.io/controller-runtime v0.11.0/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA=\nsigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=\nsigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=\nsigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=\nsigs.k8s.io/kustomize/api v0.10.1/go.mod h1:2FigT1QN6xKdcnGS2Ppp1uIWrtWN28Ms8A3OZUZhwr8=\nsigs.k8s.io/kustomize/kyaml v0.13.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=\nsigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=\nsigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=\nsigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=\nsigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=\nsigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=\nsigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=\nsigs.k8s.io/structured-merge-diff/v4 v4.2.0/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=\nsigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=\nsigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=\nsigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=\nsigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=\nsigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=\nsigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=\nsigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=\nsourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=\nstathat.com/c/consistent v1.0.0/go.mod h1:QkzMWzcbB+yQBL2AttO6sgsQS/JSTapcDISJalmCDS0=\n"
  },
  {
    "path": "helper/.goreleaser.yml",
    "content": "# .goreleaser.yml\nproject_name: dtm\nbuilds:\n  - id: dtm_amd64\n    env: [CGO_ENABLED=0]\n    goos:\n      - linux\n      - windows\n      - darwin\n    goarch:\n      - amd64\n    dir: .\n    main: main.go\n    ldflags:\n      - -s -w -X main.Version=v{{.Version}}\n  - id: dtm_arm64\n    env: [CGO_ENABLED=0]\n    goos:\n      - darwin\n    goarch:\n      - arm64\n    dir: .\n    main: main.go\n    ldflags:\n      - -s -w -X main.Version=v{{.Version}}\n"
  },
  {
    "path": "helper/Dockerfile-release",
    "content": "# syntax=docker/dockerfile:1\n# FROM node:14.19-alpine as builder1\n# ARG RELEASE_VERSION\n# WORKDIR /app/dtm\n# COPY . .\n# RUN cd admin && yarn && VITE_ADMIN_VERSION=$RELEASE_VERSION yarn build\n\nFROM --platform=amd64 node as builder2\nARG TARGETARCH\nARG TARGETOS\nARG RELEASE_VERSION\nWORKDIR /app/dtm\nCOPY . .\nRUN cd admin && yarn && VITE_ADMIN_VERSION=$RELEASE_VERSION yarn build\n\nFROM --platform=$TARGETPLATFORM golang:1.18-alpine as builder1\nARG TARGETARCH\nARG TARGETOS\nARG RELEASE_VERSION\nWORKDIR /app/dtm\n# RUN go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct\nCOPY . .\nCOPY --from=builder2 /app/dtm/admin/dist /app/dtm/admin/dist\nRUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags=\"-s -w -X main.Version=$RELEASE_VERSION\"\n\nFROM --platform=$TARGETPLATFORM alpine\nCOPY --from=builder1 /app/dtm/dtm /app/dtm/\nWORKDIR /app/dtm\nEXPOSE 8080\nENTRYPOINT [\"/app/dtm/dtm\"]\n"
  },
  {
    "path": "helper/README-cn.md",
    "content": "![license](https://img.shields.io/github/license/dtm-labs/dtm)\n![Build Status](https://github.com/dtm-labs/dtm/actions/workflows/tests.yml/badge.svg?branch=main)\n[![codecov](https://codecov.io/gh/dtm-labs/dtm/branch/main/graph/badge.svg?token=UKKEYQLP3F)](https://codecov.io/gh/dtm-labs/dtm)\n[![Go Report Card](https://goreportcard.com/badge/github.com/dtm-labs/dtm)](https://goreportcard.com/report/github.com/dtm-labs/dtm)\n[![Go Reference](https://pkg.go.dev/badge/github.com/dtm-labs/dtm.svg)](https://pkg.go.dev/github.com/dtm-labs/dtm)\n[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#database)\n\n简体中文 | [English](https://github.com/dtm-labs/dtm/blob/main/helper/README-en.md)\n\n# 跨语言分布式事务管理器\n\nDTM是一款变革性的分布式事务框架，提供了傻瓜式的使用方式，极大的降低了分布式事务的使用门槛，改变了“能不用分布式事务就不用”的行业现状，优雅的解决了服务间的数据一致性问题。\n\n## 谁在使用DTM(仅列出部分)\n[Tencent 腾讯](https://dtm.pub/other/using.html#tencent)\n\n[Bytedance 字节](https://dtm.pub/other/using.html#bytedance)\n\n[Ivydad 常青藤爸爸](https://dtm.pub/other/using.html#ivydad)\n\n[更多](https://dtm.pub/other/using.html)\n\n如果贵公司也已使用 dtm，欢迎在 [登记地址](https://github.com/dtm-labs/dtm/issues/7) 登记，仅仅为了推广，不做其它用途。\n\n## 特性\n* 支持多种语言：支持Go、Java、PHP、C#、Python、Nodejs 各种语言的SDK\n* 支持多种事务模式：SAGA、TCC、XA\n* 支持消息最终一致性：二阶段消息，比本地消息表更优雅的方案\n* 未支持 AT 事务模式，建议使用XA，详情参见[XA vs AT](https://dtm.pub/practice/at)\n* 支持多种数据库事务：Mysql、Redis、MongoDB、Postgres、TDSQL等\n* 支持多种存储引擎：Mysql（常用）、Redis（高性能）、MongoDB（规划中）\n* 支持多种微服务架构：[go-zero](https://github.com/zeromicro/go-zero)、go-kratos/kratos、polarismesh/polaris\n* 支持高可用，易水平扩展\n\n## 应用场景：\nDTM 可以应用于大量的场景下的数据一致性问题，以下是几个常见场景\n* [缓存管理](https://dtm.pub/app/cache.html)：彻底保证缓存最终一致及强一致\n* [秒杀扣库存](https://dtm.pub/app/flash.html)：极端情况下，也能保证Redis中精准的库存，和最终创建的订单完全一致，无需手动调整\n* [非单体的订单系统](https://dtm.pub/app/order.html)： 大幅简化架构\n* [事件发布/订阅](https://dtm.pub/practice/msg.html)：更好的发件箱模式\n\n## [性能测试报告](https://dtm.pub/other/performance.html)\n\n## [教程与文档](https://dtm.pub)\n\n## [各语言客户端及示例](https://dtm.pub/ref/sdk.html#go)\n\n## 快速开始\n如果您不是Go语言，可以跳转[各语言客户端及示例](https://dtm.pub/ref/sdk.html#go)，里面有相关的快速开始示例\n\n喜欢视频教程的朋友，可以访问[分布式事务教程-快速开始](https://www.bilibili.com/video/BV1fS4y1h7Tj/)\n\n### 运行dtm\n\n``` bash\ngit clone https://github.com/dtm-labs/dtm && cd dtm\ngo run main.go\n```\n\n### 启动并运行一个saga示例\n下面运行一个类似跨行转账的示例，包括两个事务分支：资金转出（TransOut)、资金转入（TransIn)。DTM保证TransIn和TransOut要么全部成功，要么全部回滚，保证最终金额的正确性。\n\n``` bash\ngit clone https://github.com/dtm-labs/dtmcli-go-sample && cd dtmcli-go-sample\ngo run main.go\n```\n\n## 接入详解\n\n### 接入代码\n``` GO\n  // 具体业务微服务地址\n  const qsBusi = \"http://localhost:8081/api/busi_saga\"\n  req := &gin.H{\"amount\": 30} // 微服务的载荷\n  // DtmServer为DTM服务的地址，是一个url\n  DtmServer := \"http://localhost:36789/api/dtmsvr\"\n  saga := dtmcli.NewSaga(DtmServer, shortuuid.New()).\n    // 添加一个TransOut的子事务，正向操作为url: qsBusi+\"/TransOut\"， 补偿操作为url: qsBusi+\"/TransOutCom\"\n    Add(qsBusi+\"/TransOut\", qsBusi+\"/TransOutCom\", req).\n    // 添加一个TransIn的子事务，正向操作为url: qsBusi+\"/TransIn\"， 补偿操作为url: qsBusi+\"/TransInCom\"\n    Add(qsBusi+\"/TransIn\", qsBusi+\"/TransInCom\", req)\n  // 提交saga事务，dtm会完成所有的子事务/回滚所有的子事务\n  err := saga.Submit()\n```\n\n成功运行后，可以看到TransOut、TransIn依次被调用，完成了整个分布式事务\n\n### 时序图\n\n上述saga分布式事务的时序图如下：\n\n<img src=\"https://pic3.zhimg.com/80/v2-b7d98659093c399e182a0173a8e549ca_1440w.jpg\" height=428 />\n\n### 失败情况\n在实际的业务中，子事务可能出现失败，例如转入的子账号被冻结导致转账失败。我们对业务代码进行修改，让TransIn的正向操作失败，然后看看结果\n\n``` go\n\tapp.POST(qsBusiAPI+\"/TransIn\", func(c *gin.Context) {\n\t\tlogger.Infof(\"TransIn\")\n\t\tc.JSON(409, \"\") // Status 409 表示失败，不再重试，直接回滚\n\t})\n```\n\n再运行这个例子，整个事务最终失败，时序图如下：\n\n<img src=\"https://pic3.zhimg.com/80/v2-8d8f1476be8a1e2e09ce97a89b4116c2_1440w.jpg\"  height=528 />\n\n在转入操作失败的情况下，TransIn和TransOut的补偿操作被执行，保证了最终的余额和转账前是一样的。\n\n### 更多示例\n关于更多quick start的例子，可以参考 [dtm-labs/quick-start-sample](https://github.com/dtm-labs/quick-start-sample)\n\n上述示例主要演示了分布式事务的流程，更多的内容，包括如何与实际的数据库对接，如何做补偿，如何做回滚等实际的例子，请参考[dtm-labs/dtm-examples](https://github.com/dtm-labs/dtm-examples)\n\n## 联系我们\n### 微信交流群\n\n如果您希望更快的获得反馈，或者更多的了解其他用户在使用过程中的各种反馈，欢迎加入我们的微信交流群\n\n请加作者的微信 yedf2008 好友或者扫码加好友，备注 `dtm` 按照指引进群\n\n![yedf2008](http://service.ivydad.com/cover/dubbingb6b5e2c0-2d2a-cd59-f7c5-c6b90aceb6f1.jpeg)\n\n欢迎使用[dtm](https://github.com/dtm-labs/dtm)，或者通过dtm学习实践分布式事务相关知识，欢迎star支持我们\n\n"
  },
  {
    "path": "helper/README-en.md",
    "content": "![license](https://img.shields.io/github/license/dtm-labs/dtm)\n![Build Status](https://github.com/dtm-labs/dtm/actions/workflows/tests.yml/badge.svg?branch=main)\n[![codecov](https://codecov.io/gh/dtm-labs/dtm/branch/main/graph/badge.svg?token=UKKEYQLP3F)](https://codecov.io/gh/dtm-labs/dtm)\n[![Go Report Card](https://goreportcard.com/badge/github.com/dtm-labs/dtm)](https://goreportcard.com/report/github.com/dtm-labs/dtm)\n[![Go Reference](https://pkg.go.dev/badge/github.com/dtm-labs/dtm.svg)](https://pkg.go.dev/github.com/dtm-labs/dtm)\n[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#database)\n\nEnglish | [简体中文](https://github.com/dtm-labs/dtm/blob/main/helper/README-cn.md)\n\n# Distributed Transactions Manager\n\n## What is DTM\n\nDTM is a distributed transaction framework which provides cross-service eventual data consistency. It provides saga, tcc, xa, 2-phase message, outbox, workflow patterns for a variety of application scenarios. It also supports multiple languages and multiple store engine to form up a transaction as following:\n\n<img alt=\"function-picture\" src=\"https://en.dtm.pub/assets/function.7d5618f8.png\" height=250 />\n\n## Who's using DTM (partial)\n\n[Tencent](https://en.dtm.pub/other/using.html#tencent)\n\n[Bytedance](https://en.dtm.pub/other/using.html#bytedance)\n\n[Ivydad](https://en.dtm.pub/other/using.html#ivydad)\n\n[More](https://en.dtm.pub/other/using.html)\n\n## Features\n* Support for multiple transaction modes: SAGA, TCC, XA, Workflow, Outbox\n* Multiple languages support: SDK for Go, Java, PHP, C#, Python, Nodejs\n* Better Outbox: 2-phase messages, a more elegant solution than Outbox, support multi-databases\n* Multiple database transaction support: Mysql, Redis, MongoDB, Postgres, TDSQL, etc.\n* Support for multiple storage engines: Mysql (common), Redis (high performance), BoltDB (dev&test), MongoDB (under planning)\n* Support for multiple microservices architectures: [go-zero](https://github.com/zeromicro/go-zero), go-kratos/kratos, polarismesh/polaris\n* Support for high availability and easy horizontal scaling\n\n## Application scenarios.\nDTM can be applied to data consistency issues in a large number of scenarios, here are a few common ones\n* [cache management](https://en.dtm.pub/app/cache.html): thoroughly guarantee the cache final consistency and strong consistency\n* [flash-sales to deduct inventory](https://en.dtm.pub/app/flash.html): in extreme cases, it is also possible to ensure that the precise inventory in Redis is exactly the same as the final order created, without the need for manual adjustment\n* [Non-monolithic order system](https://en.dtm.pub/app/order.html): Dramatically simplifies the architecture\n* [Event publishing/subscription](https://en.dtm.pub/practice/msg.html): better outbox pattern\n\n## [Cook Book](https://en.dtm.pub)\n\n## Quick start\n\n### run dtm\n\n``` bash\ngit clone https://github.com/dtm-labs/dtm && cd dtm\ngo run main.go\n```\n\n### Start an example\nSuppose we want to perform an inter-bank transfer. The operations of transfer out (TransOut) and transfer in (TransIn) are coded in separate micro-services.\n\nHere is an example to illustrate a solution of dtm to this problem:\n\n``` bash\ngit clone https://github.com/dtm-labs/quick-start-sample.git && cd quick-start-sample/workflow-grpc\ngo run main.go\n```\n\n## Code\n\n### Usage\n``` go\nwfName := \"workflow-grpc\"\nerr = workflow.Register(wfName, func(wf *workflow.Workflow, data []byte) error {\n  // ...\n  // Define a transaction branch for TransOut\n  wf.NewBranch().OnRollback(func(bb *dtmcli.BranchBarrier) error {\n    // compensation for TransOut\n    _, err := busiCli.TransOutRevert(wf.Context, &req)\n    return err\n  })\n  _, err = busiCli.TransOut(wf.Context, &req)\n  // check error\n\n  // Define another transaction branch for TransIn\n  wf.NewBranch().OnRollback(func(bb *dtmcli.BranchBarrier) error {\n    _, err := busiCli.TransInRevert(wf.Context, &req)\n    return err\n  })\n  _, err = busiCli.TransIn(wf.Context, &req)\n  return err\n}\n\n// ...\nreq := busi.BusiReq{Amount: 30, TransInResult: \"\"}\ndata, err := proto.Marshal(&req)\n\n// Execute workflow\n_, err = workflow.ExecuteCtx(wfName, shortuuid.New(), data)\nlogger.Infof(\"result of workflow.Execute is: %v\", err)\n\n```\n\nWhen the above code runs, we can see in the console that services `TransOut`, `TransIn` has been called.\n\n#### Rollback upon failure\nIf any forward operation fails, DTM invokes the corresponding compensating operation of each sub-transaction to roll back, after which the transaction is successfully rolled back.\n\nLet's purposely trigger the failure of the second sub-transaction and watch what happens\n\n``` go\n// req := busi.BusiReq{Amount: 30, TransInResult: \"\"}\nreq := busi.BusiReq{Amount: 30, TransInResult: \"FAILURE\"}\n})\n```\n\nwe can see in the console that services `TransOut`, `TransIn`, `TransOutRevert` has been called\n\n## More examples\nIf you want more quick start examples, please refer to [dtm-labs/quick-start-sample](https://github.com/dtm-labs/quick-start-sample)\n\nThe above example mainly demonstrates the flow of a distributed transaction. More on this, including practical examples of how to interact with an actual database, how to do compensation, how to do rollback, etc. please refer to [dtm-examples](https://github.com/dtm-labs/dtm-examples) for more examples.\n\n## Chat Group\n\nJoin the chat via [https://discord.gg/dV9jS5Rb33](https://discord.gg/dV9jS5Rb33).\n\n## Give a star! ⭐\n\nIf you think this project is interesting, or helpful to you, please give a star!\n"
  },
  {
    "path": "helper/bench/Makefile",
    "content": "# All targets.\ndefault: bench\n\n# configure these paths according to you system\nbench: /usr/local/bin/go /etc/redis/redis.conf /usr/local/bin/docker-compose main.go\n\trm -f ../conf.sample.yml\n\tgo build -o bench\n\ngo: /usr/local/bin/go\n\nredis: /etc/redis/redis.conf\n\nmysql: /usr/local/bin/docker-compose\n\n/usr/local/bin/go:\n\twget https://golang.org/dl/go1.17.1.linux-amd64.tar.gz\n\trm -rf /usr/local/go && tar -C /usr/local -xzf go1.17.1.linux-amd64.tar.gz && cp -f /usr/local/go/bin/go /usr/local/bin/go && rm go1.*\n\n/etc/redis/redis.conf:\n\tapt update\n\tapt install -y redis redis-tools\n\n/usr/local/bin/docker-compose:\n\tapt update\n\tapt install -y sysbench apache2-utils mysql-client-core-8.0\n\tcurl -fsSL https://get.docker.com | sh\n\tcurl -L \"https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)\" -o /usr/local/bin/docker-compose\n\tchmod +x /usr/local/bin/docker-compose\n\tcd .. && docker-compose -f helper/compose.mysql.yml up -d && cd bench\n"
  },
  {
    "path": "helper/bench/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/dtmsvr\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/config\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage/registry\"\n\t\"github.com/dtm-labs/dtm/helper/bench/svr\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/dtm-labs/logger\"\n)\n\nvar usage = `bench is a bench test server for dtmf\nusage:\n    redis   prepare for redis bench test\n    db      prepare for mysql|postgres bench test\n\t\tboltdb  prepare for boltdb bench test\n`\n\nfunc hintAndExit() {\n\tfmt.Print(usage)\n\tos.Exit(0)\n}\n\nvar conf = &config.Config\n\nfunc main() {\n\tif len(os.Args) <= 1 {\n\t\thintAndExit()\n\t}\n\tlogger.Infof(\"starting bench server\")\n\tconfig.MustLoadConfig(\"\")\n\tlogger.InitLog(conf.LogLevel)\n\tregistry.WaitStoreUp()\n\tdtmsvr.PopulateDB(false)\n\tif os.Args[1] == \"db\" {\n\t\tif busi.BusiConf.Driver == \"mysql\" {\n\t\t\tdtmcli.SetCurrentDBType(busi.BusiConf.Driver)\n\t\t\tsvr.PrepareBenchDB()\n\t\t}\n\t\tbusi.PopulateDB(false)\n\t} else if os.Args[1] == \"redis\" || os.Args[1] == \"boltdb\" {\n\n\t} else {\n\t\thintAndExit()\n\t}\n\tdtmsvr.StartSvr()\n\tgo dtmsvr.CronExpiredTrans(-1)\n\tsvr.StartSvr()\n\tselect {}\n}\n"
  },
  {
    "path": "helper/bench/prepare.sh",
    "content": "# !/bin/bash\napt update\napt install -y git\ngit clone https://github.com/dtm-labs/dtm.git && cd dtm && git checkout alpha && cd bench && make\n\n\necho 'all prepared. you shoud run following commands to test in different terminal'\necho\necho 'cd dtm && go run helper/bench/main.go redis|boltdb|db'\necho 'cd dtm && ./helper/bench/test-redis|boltdb|mysql.sh'\n"
  },
  {
    "path": "helper/bench/setup-redis6.sh",
    "content": "# !/bin/bash\napt update\napt install -y software-properties-common\nadd-apt-repository -y ppa:redislabs/redis\napt install -y redis redis-tools\n\n"
  },
  {
    "path": "helper/bench/setup.sh",
    "content": "# !/bin/bash\n\n# install all commands needed\n\napt update\napt install -y sysbench apache2-utils mysql-client-core-8.0 redis redis-tools\n\n# install docker and docker-compose\ncurl -fsSL https://get.docker.com -o get-docker.sh\nsh get-docker.sh\ncurl -L \"https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)\" -o /usr/local/bin/docker-compose\nchmod +x /usr/local/bin/docker-compose\n\n# install go\nwget https://golang.org/dl/go1.17.1.linux-amd64.tar.gz\nrm -rf /usr/local/go && tar -C /usr/local -xzf go1.17.1.linux-amd64.tar.gz && cp -f /usr/local/go/bin/go /usr/local/bin/go\n"
  },
  {
    "path": "helper/bench/svr/http.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage svr\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmsvr\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/lithammer/shortuuid/v3\"\n)\n\n// launch command：go run app/main.go qs\n\n// service address of the transcation\nconst benchAPI = \"/api/busi_bench\"\nconst total = 200000\n\nvar benchPort = dtmimp.If(os.Getenv(\"BENCH_PORT\") == \"\", \"8083\", os.Getenv(\"BENCH_PORT\")).(string)\nvar benchBusi = fmt.Sprintf(\"http://localhost:%s%s\", benchPort, benchAPI)\n\nfunc pdbGet() *sql.DB {\n\tdb, err := dtmimp.PooledDB(busi.BusiConf)\n\tlogger.FatalIfError(err)\n\treturn db\n}\n\nfunc txGet() *sql.Tx {\n\tdb := pdbGet()\n\ttx, err := db.Begin()\n\tlogger.FatalIfError(err)\n\treturn tx\n}\n\nfunc reloadData() {\n\ttime.Sleep(dtmsvr.UpdateBranchAsyncInterval * 2)\n\tbegan := time.Now()\n\tdb := pdbGet()\n\ttables := []string{\"dtm_busi.user_account\", \"dtm_busi.user_account_log\", \"dtm.trans_global\", \"dtm.trans_branch_op\", \"dtm_barrier.barrier\"}\n\tfor _, t := range tables {\n\t\t_, err := dtmimp.DBExec(busi.BusiConf.Driver, db, fmt.Sprintf(\"truncate %s\", t))\n\t\tlogger.FatalIfError(err)\n\t}\n\ts := \"insert ignore into dtm_busi.user_account(user_id, balance) values \"\n\tss := []string{}\n\tfor i := 1; i <= total; i++ {\n\t\tss = append(ss, fmt.Sprintf(\"(%d, 1000000)\", i))\n\t}\n\t_, err := dtmimp.DBExec(busi.BusiConf.Driver, db, s+strings.Join(ss, \",\"))\n\tlogger.FatalIfError(err)\n\tlogger.Debugf(\"%d users inserted. used: %dms\", total, time.Since(began).Milliseconds())\n}\n\nvar uidCounter int32\nvar mode string\nvar sqls = 1\n\n// PrepareBenchDB prepares db data for bench\nfunc PrepareBenchDB() {\n\tdb := pdbGet()\n\t_, err := dtmimp.DBExec(busi.BusiConf.Driver, db, \"CREATE DATABASE if not exists dtm_busi\")\n\tlogger.FatalIfError(err)\n\t_, err = dtmimp.DBExec(busi.BusiConf.Driver, db, \"drop table if exists dtm_busi.user_account_log\")\n\tlogger.FatalIfError(err)\n\t_, err = dtmimp.DBExec(busi.BusiConf.Driver, db, `create table if not exists dtm_busi.user_account_log (\n\tid      INT(11) AUTO_INCREMENT PRIMARY KEY,\n\tuser_id INT(11) NOT NULL,\n\tdelta DECIMAL(11, 2) not null,\n\tgid varchar(45) not null,\n\tbranch_id varchar(45) not null,\n\top varchar(45) not null,\n\treason varchar(45),\n\tcreate_time datetime not null default now(),\n\tupdate_time datetime not null default now(),\n\tkey(user_id),\n\tkey(create_time)\n)\n`)\n\tlogger.FatalIfError(err)\n}\n\n// StartSvr 1\nfunc StartSvr() {\n\tapp := dtmutil.GetGinApp()\n\tbenchAddRoute(app)\n\tlogger.Infof(\"bench listening at %s\", benchPort)\n\tgo func() {\n\t\t_ = app.Run(fmt.Sprintf(\":%s\", benchPort))\n\t}()\n}\n\nfunc qsAdjustBalance(uid int, amount int, c *gin.Context) error { // nolint: unparam\n\tif strings.Contains(mode, \"empty\") || sqls == 0 {\n\t\treturn nil\n\t}\n\ttb := dtmimp.TransBaseFromQuery(c.Request.URL.Query())\n\tf := func(tx *sql.Tx) error {\n\t\tfor i := 0; i < sqls; i++ {\n\t\t\t_, err := dtmimp.DBExec(busi.BusiConf.Driver, tx, \"insert into dtm_busi.user_account_log(user_id, delta, gid, branch_id, op, reason)  values(?,?,?,?,?,?)\",\n\t\t\t\tuid, amount, tb.Gid, c.Query(\"branch_id\"), tb.TransType, fmt.Sprintf(\"inserted by dtm transaction %s %s\", tb.Gid, c.Query(\"branch_id\")))\n\t\t\tlogger.FatalIfError(err)\n\t\t\t_, err = dtmimp.DBExec(busi.BusiConf.Driver, tx, \"update dtm_busi.user_account set balance = balance + ?, update_time = now() where user_id = ?\", amount, uid)\n\t\t\tlogger.FatalIfError(err)\n\t\t}\n\t\treturn nil\n\t}\n\tif strings.Contains(mode, \"barrier\") {\n\t\tbarrier, err := dtmcli.BarrierFromQuery(c.Request.URL.Query())\n\t\tlogger.FatalIfError(err)\n\t\terr = barrier.CallWithDB(pdbGet(), f)\n\t\tlogger.FatalIfError(err)\n\t} else {\n\t\ttx := txGet()\n\t\terr := f(tx)\n\t\tlogger.FatalIfError(err)\n\t\terr = tx.Commit()\n\t\tlogger.FatalIfError(err)\n\t}\n\n\treturn nil\n}\n\nvar stockKey = \"{a}--stock-1\"\n\nfunc benchAddRoute(app *gin.Engine) {\n\tapp.POST(benchAPI+\"/TransIn\", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {\n\t\treturn qsAdjustBalance(dtmimp.MustAtoi(c.Query(\"uid\")), 1, c)\n\t}))\n\tapp.POST(benchAPI+\"/TransInCom\", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {\n\t\treturn qsAdjustBalance(dtmimp.MustAtoi(c.Query(\"uid\")), -1, c)\n\t}))\n\tapp.POST(benchAPI+\"/TransOut\", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {\n\t\treturn qsAdjustBalance(dtmimp.MustAtoi(c.Query(\"uid\")), -1, c)\n\t}))\n\tapp.POST(benchAPI+\"/TransOutCom\", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {\n\t\treturn qsAdjustBalance(dtmimp.MustAtoi(c.Query(\"uid\")), 30, c)\n\t}))\n\tapp.Any(benchAPI+\"/reloadData\", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {\n\t\treloadData()\n\t\tmode = c.Query(\"m\")\n\t\ts := c.Query(\"sqls\")\n\t\tif s != \"\" {\n\t\t\tsqls = dtmimp.MustAtoi(s)\n\t\t}\n\t\treturn nil\n\t}))\n\tapp.Any(benchAPI+\"/bench\", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {\n\t\tuid := (atomic.AddInt32(&uidCounter, 1)-1)%total + 1\n\t\tsuid := fmt.Sprintf(\"%d\", uid)\n\t\tsuid2 := fmt.Sprintf(\"%d\", total+1-uid)\n\t\treq := gin.H{}\n\t\tparams := fmt.Sprintf(\"?uid=%s\", suid)\n\t\tparams2 := fmt.Sprintf(\"?uid=%s\", suid2)\n\t\tlogger.Debugf(\"mode: %s contains dtm: %t\", mode, strings.Contains(mode, \"dtm\"))\n\t\tif strings.Contains(mode, \"dtm\") {\n\t\t\tsaga := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, fmt.Sprintf(\"bench-%d\", uid)).\n\t\t\t\tAdd(benchBusi+\"/TransOut\"+params, benchBusi+\"/TransOutCom\"+params, req).\n\t\t\t\tAdd(benchBusi+\"/TransIn\"+params2, benchBusi+\"/TransInCom\"+params2, req)\n\t\t\tsaga.WaitResult = true\n\t\t\terr := saga.Submit()\n\t\t\tdtmimp.E2P(err)\n\t\t} else {\n\t\t\t_, err := dtmcli.GetRestyClient().R().SetBody(gin.H{}).SetQueryParam(\"uid\", suid2).Post(benchBusi + \"/TransOut\")\n\t\t\tdtmimp.E2P(err)\n\t\t\t_, err = dtmcli.GetRestyClient().R().SetBody(gin.H{}).SetQueryParam(\"uid\", suid).Post(benchBusi + \"/TransIn\")\n\t\t\tdtmimp.E2P(err)\n\t\t}\n\t\treturn nil\n\t}))\n\tapp.Any(benchAPI+\"/benchEmptyUrl\", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {\n\t\tgid := shortuuid.New()\n\t\treq := gin.H{}\n\t\tsaga := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, gid).\n\t\t\tAdd(\"\", \"\", req).\n\t\t\tAdd(\"\", \"\", req)\n\t\tsaga.WaitResult = true\n\t\treturn saga.Submit()\n\t}))\n\tapp.Any(benchAPI+\"/benchFlashSalesReset\", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {\n\t\t_, err := busi.RedisGet().FlushAll(context.Background()).Result()\n\t\tlogger.FatalIfError(err)\n\t\t_, err = busi.RedisGet().Set(context.Background(), stockKey, \"0\", 86400*time.Second).Result()\n\t\tlogger.FatalIfError(err)\n\t\treturn nil\n\t}))\n\tapp.Any(benchAPI+\"/benchFlashSales\", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {\n\t\tgid := \"{a}-\" + shortuuid.New()\n\t\tmsg := dtmcli.NewMsg(dtmutil.DefaultHTTPServer, gid).\n\t\t\tAdd(\"\", nil)\n\t\treturn msg.DoAndSubmit(\"\", func(bb *dtmcli.BranchBarrier) error {\n\t\t\treturn bb.RedisCheckAdjustAmount(busi.RedisGet(), stockKey, -1, 86400)\n\t\t})\n\t}))\n}\n"
  },
  {
    "path": "helper/bench/test-boltdb.sh",
    "content": "# !/bin/bash\n\nset -x\n\nab -n 50000 -c 10 \"http://127.0.0.1:8083/api/busi_bench/benchEmptyUrl\"\n"
  },
  {
    "path": "helper/bench/test-flash-sales.sh",
    "content": "# !/bin/bash\n\nset -x\n\nexport LOG_LEVEL=fatal\nexport STORE_DRIVER=redis\nexport STORE_HOST=localhost\nexport STORE_PORT=6379\nexport BUSI_REDIS=localhost:6379\n./bench redis &\necho 'sleeping 3s for dtm bench to run up.' && sleep 3\ncurl \"http://127.0.0.1:8083/api/busi_bench/benchFlashSalesReset\"\nab -n 300000 -c 20 \"http://127.0.0.1:8083/api/busi_bench/benchFlashSales\"\npkill bench\n"
  },
  {
    "path": "helper/bench/test-mysql.sh",
    "content": "# !/bin/bash\n\nset -x\n\ncd /usr/share/sysbench/\necho 'create database sbtest;' > mysql -h 127.0.0.1 -uroot\n\nsysbench oltp_write_only.lua --time=60 --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=root --mysql-password= --mysql-db=sbtest --table-size=1000000 --tables=10 --threads=10 --events=999999999 --report-interval=10 prepare\n\nsysbench oltp_write_only.lua --time=60 --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=root --mysql-password= --mysql-db=sbtest --table-size=1000000 --tables=10 --threads=10 --events=999999999 --report-interval=10 run\n\nexport TIME=10\nexport CONCURRENT=20\ncurl \"http://127.0.0.1:8083/api/busi_bench/reloadData?m=dtm_tx&sqls=0\" && ab -t $TIME -c $CONCURRENT \"http://127.0.0.1:8083/api/busi_bench/bench\"\ncurl \"http://127.0.0.1:8083/api/busi_bench/reloadData?m=dtm_tx&sqls=5\" && ab -t $TIME -c $CONCURRENT \"http://127.0.0.1:8083/api/busi_bench/bench\"\ncurl \"http://127.0.0.1:8083/api/busi_bench/reloadData?m=dtm_barrier&sqls=5\" && ab -t $TIME -c $CONCURRENT \"http://127.0.0.1:8083/api/busi_bench/bench\"\ncurl \"http://127.0.0.1:8083/api/busi_bench/reloadData?m=raw_tx&sqls=5\" && ab -t $TIME -c $CONCURRENT \"http://127.0.0.1:8083/api/busi_bench/bench\"\ncurl \"http://127.0.0.1:8083/api/busi_bench/reloadData?m=dtm_tx&sqls=1\" && ab -t $TIME -c $CONCURRENT \"http://127.0.0.1:8083/api/busi_bench/bench\"\ncurl \"http://127.0.0.1:8083/api/busi_bench/reloadData?m=dtm_barrier&sqls=1\" && ab -t $TIME -c $CONCURRENT \"http://127.0.0.1:8083/api/busi_bench/bench\"\ncurl \"http://127.0.0.1:8083/api/busi_bench/reloadData?m=raw_tx&sqls=1\" && ab -t $TIME -c $CONCURRENT \"http://127.0.0.1:8083/api/busi_bench/bench\"\ncurl \"http://127.0.0.1:8083/api/busi_bench/reloadData?m=raw_empty\" && ab -t $TIME -c $CONCURRENT \"http://127.0.0.1:8083/api/busi_bench/bench\"\n\n"
  },
  {
    "path": "helper/bench/test-redis.sh",
    "content": "# !/bin/bash\n\nset -x\n\nexport LOG_LEVEL=warn\nexport STORE_DRIVER=redis\nexport STORE_HOST=localhost\nexport STORE_PORT=6379\ncd .. && bench/bench redis &\necho 'sleeping 3s for dtm bench to run up.' && sleep 3\nab -n 1000000 -c 10 \"http://127.0.0.1:8083/api/busi_bench/benchEmptyUrl\"\npkill bench\n\nredis-benchmark -n 300000 SET 'abcdefg' 'ddddddd'\n\nredis-benchmark -n 300000 EVAL \"redis.call('SET', 'abcdedf', 'ddddddd')\" 0\n\nredis-benchmark -n 300000 EVAL \"redis.call('SET', KEYS[1], ARGV[1])\" 1 'aaaaaaaaa' 'bbbbbbbbbb'\n\nredis-benchmark -n 3000000 -P 50 SET 'abcdefg' 'ddddddd'\n\nredis-benchmark -n 300000 EVAL \"for k=1, 10 do; redis.call('SET', KEYS[1], ARGV[1]);end\" 1 'aaaaaaaaa' 'bbbbbbbbbb'\n\nredis-benchmark -n 300000 -P 50 EVAL \"redis.call('SET', KEYS[1], ARGV[1])\" 1 'aaaaaaaaa' 'bbbbbbbbbb'\n\nredis-benchmark -n 300000 EVAL \"for k=1,10 do;local c = cjson.decode(ARGV[1]);end\" 1 'aaaaaaaaa' '{\"aaaaa\":\"bbbbb\",\"b\":1,\"t\":\"2012-01-01 14:00:00\"}'\n\n"
  },
  {
    "path": "helper/compose.store.yml",
    "content": "version: '3.3'\nservices:\n  mysql:\n    image: 'mysql:8'\n    volumes:\n      - /etc/localtime:/etc/localtime:ro\n    environment:\n      MYSQL_ALLOW_EMPTY_PASSWORD: 1\n    command:\n      [\n        '--character-set-server=utf8mb4',\n        '--collation-server=utf8mb4_unicode_ci',\n      ]\n    ports:\n      - '3306:3306'\n  postgres:\n    image: 'postgres:13'\n    command: postgres --max_prepared_transactions=1000\n    volumes:\n      - /etc/localtime:/etc/localtime:ro\n    environment:\n      POSTGRES_PASSWORD: mysecretpassword\n      POSTGRES_DB: dtm\n\n    ports:\n      - '5432:5432'\n  redis:\n    image: 'redis'\n    volumes:\n      - /etc/localtime:/etc/localtime:ro\n    ports:\n      - '6379:6379'\n  mongo:\n    image: yedf/mongo-rs\n    volumes:\n      - /etc/localtime:/etc/localtime:ro\n    ports:\n      - '27017:27017'\n  sqlserver2019:\n    image: mcr.microsoft.com/mssql/server:2019-latest\n    volumes:\n      - /etc/localtime:/etc/localtime:ro\n    ports:\n      - 1433:1433\n    environment:\n      - ACCEPT_EULA=Y\n      - MSSQL_SA_PASSWORD=p@ssw0rd\n"
  },
  {
    "path": "helper/golint.sh",
    "content": "set -x\n\ngo install github.com/mgechev/revive@latest && revive -config revive.toml ./...\n"
  },
  {
    "path": "helper/sync-client.sh",
    "content": "#! /bin/bash\nset -x\nver=$1\nif [ x$ver == x ]; then\n  echo please specify you version like vx.x.x;\n  exit 1;\nfi\n\nif [ ${ver:0:1} != v ]; then\n  echo please specify you version like vx.x.x;\n  exit 1;\nfi\n\ncd ../client\ncp -rf ../dtm/client/* ./\nsed -i '' -e 's/dtm-labs\\/dtm\\//dtm-labs\\//g' */*.go */*/*.go\n\nrm -rf */*_test.go */*/*_test.go */*log */*/*log\ngo mod tidy\ngo build || exit 1\n\ngit add .\ngit commit -m\"update from dtm to version $ver\"\ngit push\ngit tag $ver\ngit push --tags\n\ncd ../quick-start-sample\n\ngo get -u github.com/dtm-labs/client@$ver\ngo mod tidy\ngo build || exit 1\ngit add .\ngit commit -m\"update from dtm to version $ver\"\ngit push\n\n"
  },
  {
    "path": "helper/test-cover.sh",
    "content": "set -x\nexport DTM_DEBUG=1\necho \"mode: count\" > coverage.txt\nfor store in redis boltdb mysql postgres sqlserver; do\n  TEST_STORE=$store go test -failfast -covermode count -coverprofile=profile.out -coverpkg=github.com/dtm-labs/dtm/client/dtmcli,github.com/dtm-labs/dtm/client/dtmcli/dtmimp,github.com/dtm-labs/logger,github.com/dtm-labs/dtm/client/dtmgrpc,github.com/dtm-labs/dtm/client/workflow,github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp,github.com/dtm-labs/dtm/dtmsvr,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 ./... || exit 1\n    echo \"TEST_STORE=$store finished\"\n    if [ -f profile.out ]; then\n        cat profile.out | grep -v 'mode:' >> coverage.txt\n        echo > profile.out\n    fi\ndone\n## for local unit test, you may use following command\n# SKIP_MONGO=1 TEST_STORE=redis GOARCH=amd64 go test -v -failfast -count=1  -gcflags=all=-l ./...\n\n# go tool cover -html=coverage.txt\n\n# curl -s https://codecov.io/bash | bash\n"
  },
  {
    "path": "main.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n// package main is the entry of dtm server\npackage main\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"embed\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"net/url\"\n\t\"os\"\n\t\"os/signal\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\n\t\"github.com/dtm-labs/dtm/dtmsvr/config\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/entry\"\n\t_ \"github.com/dtm-labs/dtm/dtmsvr/microservices\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// Version defines version info. It is set by -ldflags.\nvar Version string\n\nfunc main() {\n\tapp, conf := entry.Main(&Version)\n\tif app != nil {\n\t\taddAdmin(app, conf)\n\t\tq := make(chan os.Signal, 1)\n\t\tsignal.Notify(q, syscall.SIGINT, syscall.SIGTERM)\n\t\t<-q\n\t\tlogger.Infof(\"Shutdown dtm server...\")\n\t}\n}\n\n//go:embed admin/dist\nvar admin embed.FS\n\nvar target = \"\"\n\nfunc getSub(f1 fs.FS, subs ...string) fs.FS {\n\tvar err error\n\tfor _, sub := range subs {\n\t\tf1, err = fs.Sub(f1, sub)\n\t\tlogger.FatalIfError(err)\n\t}\n\treturn f1\n}\nfunc addAdmin(app *gin.Engine, conf *config.Type) {\n\t// for released dtm, serve admin from local files because the build output has been embed\n\t// for testing users, proxy admin to target because the build output has not been embed\n\tdist := getSub(admin, \"admin\", \"dist\")\n\tindex, err := dist.Open(\"index.html\")\n\n\tvar router gin.IRoutes = app\n\tif len(conf.AdminBasePath) > 0 {\n\t\trouter = app.Group(conf.AdminBasePath)\n\t}\n\tif err == nil {\n\t\tcont, err := io.ReadAll(index)\n\t\tlogger.FatalIfError(err)\n\t\t_ = index.Close()\n\t\tcont = bytesTryReplaceIndex(cont, conf)\n\n\t\trenderIndex := func(c *gin.Context) {\n\t\t\tc.Data(200, \"text/html; charset=utf-8\", cont)\n\t\t}\n\t\trouter.StaticFS(\"/assets\", http.FS(getSub(dist, \"assets\")))\n\t\trouter.GET(\"/admin/*name\", renderIndex)\n\t\trouter.GET(\"/\", renderIndex)\n\t\trouter.GET(\"/favicon.ico\", func(ctx *gin.Context) {\n\t\t\thttp.StripPrefix(conf.AdminBasePath, http.FileServer(http.FS(dist))).ServeHTTP(ctx.Writer, ctx.Request)\n\t\t})\n\t\tlogger.Infof(\"admin is served from dir 'admin/dist/'\")\n\t} else {\n\t\trouter.GET(\"/\", proxyAdmin(conf))\n\t\trouter.GET(\"/assets/*name\", proxyAdmin(conf))\n\t\trouter.GET(\"/admin/*name\", proxyAdmin(conf))\n\t\tlang := os.Getenv(\"LANG\")\n\t\tif strings.HasPrefix(lang, \"zh_CN\") {\n\t\t\ttarget = \"cn-admin.dtm.pub\"\n\t\t} else {\n\t\t\ttarget = \"admin.dtm.pub\"\n\t\t}\n\t\tlogger.Infof(\"admin is proxied to %s\", target)\n\t}\n\tlogger.Infof(\"admin is running at: http://localhost:%d%s\", conf.HTTPPort, conf.AdminBasePath)\n}\n\nfunc proxyAdmin(conf *config.Type) func(c *gin.Context) {\n\treturn func(c *gin.Context) {\n\t\tu := &url.URL{}\n\t\tu.Scheme = \"http\"\n\t\tu.Host = target\n\t\tproxy := httputil.NewSingleHostReverseProxy(u)\n\t\toriginalDirector := proxy.Director\n\t\tproxy.Director = func(r *http.Request) {\n\t\t\toriginalDirector(r)\n\t\t\tp := strings.TrimPrefix(r.URL.Path, conf.AdminBasePath)\n\t\t\trp := strings.TrimPrefix(r.URL.RawPath, conf.AdminBasePath)\n\t\t\tr.URL.Path = p\n\t\t\tr.URL.RawPath = rp\n\t\t}\n\t\tproxy.Transport = &transport{RoundTripper: http.DefaultTransport, conf: conf}\n\t\tproxy.ErrorHandler = func(rw http.ResponseWriter, req *http.Request, err error) {\n\t\t\tlogger.Warnf(\"http: proxy error: %v\", err)\n\t\t\tret := fmt.Sprintf(\"http proxy error %v\", err)\n\t\t\t_, _ = rw.Write([]byte(ret))\n\t\t}\n\t\tlogger.Debugf(\"proxy admin to %s\", target)\n\t\tc.Request.Host = target\n\t\tproxy.ServeHTTP(c.Writer, c.Request)\n\t}\n\n}\n\n// bytesTryReplaceIndex replace index.html base path\nfunc bytesTryReplaceIndex(source []byte, conf *config.Type) []byte {\n\tsource = bytes.Replace(source, []byte(\"\\\"assets/\"), []byte(\"\\\"\"+conf.AdminBasePath+\"/assets/\"), -1)\n\tsource = bytes.Replace(source, []byte(\"PUBLIC-PATH-VARIABLE\"), []byte(conf.AdminBasePath), -1)\n\treturn source\n}\n\ntype transport struct {\n\thttp.RoundTripper\n\tconf *config.Type\n}\n\nfunc (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {\n\tresp, err = t.RoundTripper.RoundTrip(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t//modify html only\n\tif !strings.Contains(resp.Header.Get(\"Content-Type\"), \"text/html\") {\n\t\treturn resp, err\n\t}\n\tvar reader io.ReadCloser\n\tswitch resp.Header.Get(\"Content-Encoding\") {\n\tcase \"gzip\":\n\t\treader, err = gzip.NewReader(resp.Body)\n\t\tdefer func() {\n\t\t\tif tmpErr := reader.Close(); err == nil && tmpErr != nil {\n\t\t\t\terr = tmpErr\n\t\t\t}\n\t\t}()\n\tdefault:\n\t\treader = resp.Body\n\t}\n\tdelete(resp.Header, \"Content-Encoding\")\n\tb, err := io.ReadAll(reader)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\terr = resp.Body.Close()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tb = bytesTryReplaceIndex(b, t.conf)\n\tbody := io.NopCloser(bytes.NewReader(b))\n\tresp.Body = body\n\tresp.ContentLength = int64(len(b))\n\tresp.Header.Set(\"Content-Length\", strconv.Itoa(len(b)))\n\treturn resp, nil\n}\n"
  },
  {
    "path": "qs/main.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage main\n\nimport (\n\t\"github.com/dtm-labs/dtm/test/busi\"\n)\n\nfunc main() {\n\tbusi.QsMain()\n}\n"
  },
  {
    "path": "revive.toml",
    "content": "ignoreGeneratedHeader = false\nseverity = \"warning\"\nconfidence = 0.8\nerrorCode = 0\nwarningCode = 0\n\n[rule.blank-imports]\n[rule.context-as-argument]\n[rule.context-keys-type]\n[rule.dot-imports]\n[rule.error-return]\n[rule.error-strings]\n[rule.error-naming]\n[rule.exported]\n[rule.if-return]\n[rule.increment-decrement]\n[rule.var-naming]\n[rule.var-declaration]\n[rule.range]\n[rule.receiver-naming]\n[rule.time-naming]\n[rule.unexported-return]\n[rule.indent-error-flow]\n[rule.errorf]\n[rule.superfluous-else]\n[rule.unreachable-code]\n[rule.redefines-builtin-id]"
  },
  {
    "path": "sqls/busi.mongo.js",
    "content": "use dtm_busi\ndb.user_account.drop()\ndb.user_account.createIndex({ user_id: NumberLong(1) }, { unique: true })\n\ndb.user_account.insert({ user_id: NumberLong(1), balance: 10000 })\ndb.user_account.insert({ user_id: NumberLong(2), balance: 10000 })\n"
  },
  {
    "path": "sqls/busi.mysql.sql",
    "content": "CREATE DATABASE if not exists dtm_busi\n/*!40100 DEFAULT CHARACTER SET utf8mb4 */\n;\ndrop table if exists dtm_busi.user_account;\ncreate table if not exists dtm_busi.user_account(\n  id int(11) PRIMARY KEY AUTO_INCREMENT,\n  user_id int(11) UNIQUE,\n  balance DECIMAL(10, 2) not null default '0',\n  trading_balance DECIMAL(10, 2) not null default '0',\n  create_time datetime DEFAULT now(),\n  update_time datetime DEFAULT now(),\n  key(create_time),\n  key(update_time)\n) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;\ninsert into dtm_busi.user_account (user_id, balance)\nvalues (1, 10000),\n  (2, 10000) on DUPLICATE KEY\nUPDATE balance =\nvalues (balance);"
  },
  {
    "path": "sqls/busi.postgres.sql",
    "content": "CREATE SCHEMA if not exists dtm_busi\n/* SQLINES DEMO *** RACTER SET utf8mb4 */\n;\ndrop table if exists dtm_busi.user_account;\n-- SQLINES LICENSE FOR EVALUATION USE ONLY\ncreate sequence if not exists dtm_busi.user_account_seq;\ncreate table if not exists dtm_busi.user_account(\n  id int PRIMARY KEY DEFAULT NEXTVAL ('dtm_busi.user_account_seq'),\n  user_id int UNIQUE,\n  balance DECIMAL(10, 2) not null default '0',\n  trading_balance DECIMAL(10, 2) not null default '0',\n  create_time timestamp(0) with time zone DEFAULT now(),\n  update_time timestamp(0) with time zone DEFAULT now()\n);\n-- SQLINES LICENSE FOR EVALUATION USE ONLY\ncreate index if not exists create_idx on dtm_busi.user_account(create_time);\n-- SQLINES LICENSE FOR EVALUATION USE ONLY\ncreate index if not exists update_idx on dtm_busi.user_account(update_time);\nTRUNCATE dtm_busi.user_account;\ninsert into dtm_busi.user_account (user_id, balance)\nvalues (1, 10000),\n  (2, 10000);"
  },
  {
    "path": "sqls/dtmcli.barrier.mongo.js",
    "content": "use dtm_barrier\ndb.barrier.drop()\ndb.barrier.createIndex({ gid: 1, branch_id: 1, op: 1, barrier_id: 1 }, { unique: true })\ndb.barrier.insert({ gid: \"123\", branch_id: \"01\", op: \"action\", barrier_id: \"01\", reason: \"test\" });\n"
  },
  {
    "path": "sqls/dtmcli.barrier.mysql.sql",
    "content": "create database if not exists dtm_barrier\n/*!40100 DEFAULT CHARACTER SET utf8mb4 */\n;\ndrop table if exists dtm_barrier.barrier;\ncreate table if not exists dtm_barrier.barrier(\n  id bigint(22) PRIMARY KEY AUTO_INCREMENT,\n  trans_type varchar(45) default '',\n  gid varchar(128) default '',\n  branch_id varchar(128) default '',\n  op varchar(45) default '',\n  barrier_id varchar(45) default '',\n  reason varchar(45) default '' comment 'the branch type who insert this record',\n  create_time datetime DEFAULT now(),\n  update_time datetime DEFAULT now(),\n  key(create_time),\n  key(update_time),\n  UNIQUE key(gid, branch_id, op, barrier_id)\n) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;"
  },
  {
    "path": "sqls/dtmcli.barrier.postgres.sql",
    "content": "create schema if not exists dtm_barrier;\ndrop table if exists dtm_barrier.barrier;\nCREATE SEQUENCE if not EXISTS dtm_barrier.barrier_seq;\ncreate table if not exists dtm_barrier.barrier(\n  id bigint NOT NULL DEFAULT NEXTVAL ('dtm_barrier.barrier_seq'),\n  trans_type varchar(45) default '',\n  gid varchar(128) default '',\n  branch_id varchar(128) default '',\n  op varchar(45) default '',\n  barrier_id varchar(45) default '',\n  reason varchar(45) default '',\n  create_time timestamp(0) with time zone DEFAULT NULL,\n  update_time timestamp(0) with time zone DEFAULT NULL,\n  PRIMARY KEY(id),\n  CONSTRAINT uniq_barrier unique(gid, branch_id, op, barrier_id)\n);"
  },
  {
    "path": "sqls/dtmsvr.storage.mysql.sql",
    "content": "CREATE DATABASE IF NOT EXISTS dtm\n/*!40100 DEFAULT CHARACTER SET utf8mb4 */\n;\ndrop table IF EXISTS dtm.trans_global;\nCREATE TABLE if not EXISTS dtm.trans_global (\n  `id` bigint(22) NOT NULL AUTO_INCREMENT,\n  `gid` varchar(128) NOT NULL COMMENT 'global transaction id',\n  `trans_type` varchar(45) not null COMMENT 'transaction type: saga | xa | tcc | msg',\n  `status` varchar(12) NOT NULL COMMENT 'transaction status: prepared | submitted | aborting | succeed | failed',\n  `query_prepared` varchar(1024) NOT NULL COMMENT 'url to check for msg|workflow',\n  `protocol` varchar(45) not null comment 'protocol: http | grpc | json-rpc',\n  `create_time` datetime DEFAULT NULL,\n  `update_time` datetime DEFAULT NULL,\n  `finish_time` datetime DEFAULT NULL,\n  `rollback_time` datetime DEFAULT NULL,\n  `options` varchar(1024) DEFAULT '' COMMENT 'options for transaction like: TimeoutToFail, RequestTimeout',\n  `custom_data` varchar(1024) DEFAULT '' COMMENT 'custom data for transaction',\n  `next_cron_interval` int(11) default null comment 'next cron interval. for use of cron job',\n  `next_cron_time` datetime default null comment 'next time to process this trans. for use of cron job',\n  `owner` varchar(128) not null default '' comment 'who is locking this trans',\n  `ext_data` TEXT comment 'extra data for this trans. currently used in workflow pattern',\n  `result` varchar(1024) DEFAULT '' COMMENT 'result for transaction',\n  `rollback_reason` varchar(1024) DEFAULT '' COMMENT 'rollback reason for transaction',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `gid` (`gid`),\n  key `owner`(`owner`),\n  key `status_next_cron_time` (`status`, `next_cron_time`) comment 'cron job will use this index to query trans'\n) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;\ndrop table IF EXISTS dtm.trans_branch_op;\nCREATE TABLE IF NOT EXISTS dtm.trans_branch_op (\n  `id` bigint(22) NOT NULL AUTO_INCREMENT,\n  `gid` varchar(128) NOT NULL COMMENT 'global transaction id',\n  `url` varchar(1024) NOT NULL COMMENT 'the url of this op',\n  `data` TEXT COMMENT 'request body, depreceated',\n  `bin_data` BLOB COMMENT 'request body',\n  `branch_id` VARCHAR(128) NOT NULL COMMENT 'transaction branch ID',\n  `op` varchar(45) NOT NULL COMMENT 'transaction operation type like: action | compensate | try | confirm | cancel',\n  `status` varchar(45) NOT NULL COMMENT 'transaction op status: prepared | succeed | failed',\n  `finish_time` datetime DEFAULT NULL,\n  `rollback_time` datetime DEFAULT NULL,\n  `create_time` datetime DEFAULT NULL,\n  `update_time` datetime DEFAULT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `gid_uniq` (`gid`, `branch_id`, `op`)\n) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;\ndrop table IF EXISTS dtm.kv;\nCREATE TABLE IF NOT EXISTS dtm.kv (\n  `id` bigint(22) NOT NULL AUTO_INCREMENT,\n  `cat` varchar(45) NOT NULL COMMENT 'the category of this data',\n  `k` varchar(128) NOT NULL,\n  `v` TEXT,\n  `version` bigint(22) default 1 COMMENT 'version of the value',\n  create_time datetime default NULL,\n  update_time datetime DEFAULT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE key `uniq_k`(`cat`, `k`)\n) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;\n"
  },
  {
    "path": "sqls/dtmsvr.storage.postgres.sql",
    "content": "drop table IF EXISTS trans_global;\n\nCREATE SEQUENCE if not EXISTS trans_global_seq;\nCREATE TABLE if not EXISTS trans_global (\n  id bigint NOT NULL DEFAULT NEXTVAL ('trans_global_seq'),\n  gid varchar(128) NOT NULL,\n  trans_type varchar(45) not null,\n  status varchar(45) NOT NULL,\n  query_prepared varchar(1024) NOT NULL,\n  protocol varchar(45) not null,\n  create_time timestamp(0) with time zone DEFAULT NULL,\n  update_time timestamp(0) with time zone DEFAULT NULL,\n  finish_time timestamp(0) with time zone DEFAULT NULL,\n  rollback_time timestamp(0) with time zone DEFAULT NULL,\n  options varchar(1024) DEFAULT '',\n  custom_data varchar(1024) DEFAULT '',\n  next_cron_interval int default null,\n  next_cron_time timestamp(0) with time zone default null,\n  owner varchar(128) not null default '',\n  ext_data text,\n  result varchar(1024) DEFAULT '',\n  rollback_reason varchar(1024) DEFAULT '',\n  PRIMARY KEY (id),\n  CONSTRAINT gid UNIQUE (gid)\n);\ncreate index if not EXISTS owner on trans_global(owner);\ncreate index if not EXISTS status_next_cron_time on trans_global (status, next_cron_time);\ndrop table IF EXISTS trans_branch_op;\n\nCREATE SEQUENCE if not EXISTS trans_branch_op_seq;\nCREATE TABLE IF NOT EXISTS trans_branch_op (\n  id bigint NOT NULL DEFAULT NEXTVAL ('trans_branch_op_seq'),\n  gid varchar(128) NOT NULL,\n  url varchar(1024) NOT NULL,\n  data TEXT,\n  bin_data bytea,\n  branch_id VARCHAR(128) NOT NULL,\n  op varchar(45) NOT NULL,\n  status varchar(45) NOT NULL,\n  finish_time timestamp(0) with time zone DEFAULT NULL,\n  rollback_time timestamp(0) with time zone DEFAULT NULL,\n  create_time timestamp(0) with time zone DEFAULT NULL,\n  update_time timestamp(0) with time zone DEFAULT NULL,\n  PRIMARY KEY (id),\n  CONSTRAINT gid_branch_uniq UNIQUE (gid, branch_id, op)\n);\ndrop table IF EXISTS kv;\n\nCREATE SEQUENCE if not EXISTS kv_seq;\nCREATE TABLE IF NOT EXISTS kv (\n    id bigint NOT NULL DEFAULT NEXTVAL ('kv_seq'),\n    cat varchar(45) NOT NULL,\n    k varchar(128) NOT NULL,\n    v TEXT,\n    version bigint default 1,\n    create_time timestamp(0) with time zone DEFAULT NULL,\n    update_time timestamp(0) with time zone DEFAULT  NULL,\n    PRIMARY KEY (id),\n    CONSTRAINT uniq_k UNIQUE(cat, k)\n);"
  },
  {
    "path": "sqls/dtmsvr.storage.sqlserver.sql",
    "content": "if db_id('dtm') is null\nbegin \n\tCREATE DATABASE dtm\nend;\n\ndrop table IF EXISTS dtm.dbo.trans_global;\nCREATE TABLE dtm.dbo.trans_global (\n  id bigint NOT NULL IDENTITY,\n  gid varchar(128) NOT NULL , -- COMMENT 'global transaction id',\n  trans_type varchar(45) not null , -- COMMENT 'transaction type: saga | xa | tcc | msg',\n  status varchar(12) NOT NULL , -- COMMENT 'transaction status: prepared | submitted | aborting | succeed | failed',\n  query_prepared varchar(1024) NOT NULL , -- COMMENT 'url to check for msg|workflow',\n  protocol varchar(45) not null , -- COMMENT 'protocol: http | grpc | json-rpc',\n  create_time datetimeoffset DEFAULT NULL,\n  update_time datetimeoffset DEFAULT NULL,\n  finish_time datetimeoffset DEFAULT NULL,\n  rollback_time datetimeoffset DEFAULT NULL,\n  options varchar(1024) DEFAULT '' , -- COMMENT 'options for transaction like: TimeoutToFail, RequestTimeout',\n  custom_data varchar(1024) DEFAULT '' , -- COMMENT 'custom data for transaction',\n  next_cron_interval int default null , -- COMMENT 'next cron interval. for use of cron job',\n  next_cron_time datetimeoffset default null , -- COMMENT 'next time to process this trans. for use of cron job',\n  owner varchar(128) not null default '' , -- COMMENT 'who is locking this trans',\n  ext_data VARCHAR(max) , -- COMMENT 'extra data for this trans. currently used in workflow pattern',\n  result varchar(1024) DEFAULT '' , -- COMMENT 'result for transaction',\n  rollback_reason varchar(1024) DEFAULT '' , -- COMMENT 'rollback reason for transaction',\n  PRIMARY KEY (id),\n  CONSTRAINT gid UNIQUE (gid) WITH(IGNORE_DUP_KEY = ON)\n);\nCREATE INDEX[owner] ON [dtm].[dbo].[trans_global]([owner] ASC)\nCREATE INDEX[status_next_cron_time] ON [dtm].[dbo].[trans_global]([status] ASC, next_cron_time ASC) ---- COMMENT 'cron job will use this index to query trans'\n\ndrop table IF EXISTS dtm.dbo.trans_branch_op;\nCREATE TABLE dtm.dbo.trans_branch_op (\n  id bigint NOT NULL IDENTITY,\n  gid varchar(128) NOT NULL , -- COMMENT 'global transaction id',\n  url varchar(1024) NOT NULL , -- COMMENT 'the url of this op',\n  data VARCHAR(max) , -- COMMENT 'request body, depreceated',\n  bin_data VARBINARY(max) , -- COMMENT 'request body',\n  branch_id VARCHAR(128) NOT NULL , -- COMMENT 'transaction branch ID',\n  op varchar(45) NOT NULL , -- COMMENT 'transaction operation type like: action | compensate | try | confirm | cancel',\n  status varchar(45) NOT NULL , -- COMMENT 'transaction op status: prepared | succeed | failed',\n  finish_time datetimeoffset DEFAULT NULL,\n  rollback_time datetimeoffset DEFAULT NULL,\n  create_time datetimeoffset DEFAULT NULL,\n  update_time datetimeoffset DEFAULT NULL,\n  PRIMARY KEY (id),\n  CONSTRAINT gid_uniq UNIQUE  (gid, branch_id, op)\n);\ndrop table IF EXISTS dtm.dbo.kv;\nCREATE TABLE dtm.dbo.kv (\n  id bigint NOT NULL IDENTITY,\n  cat varchar(45) NOT NULL , -- COMMENT 'the category of this data',\n  k varchar(128) NOT NULL,\n  v VARCHAR(max),\n  version bigint default 1 , -- COMMENT 'version of the value',\n  create_time datetimeoffset default NULL,\n  update_time datetimeoffset DEFAULT NULL,\n  PRIMARY KEY (id),\n  CONSTRAINT uniq_k UNIQUE (cat, k)\n);\n"
  },
  {
    "path": "sqls/dtmsvr.storage.tdsql.sql",
    "content": "CREATE DATABASE IF NOT EXISTS dtm\n/*!40100 DEFAULT CHARACTER SET utf8mb4 */\n;\ndrop table IF EXISTS dtm.trans_global;\nCREATE TABLE if not EXISTS dtm.trans_global (\n  `id` bigint(22) NOT NULL AUTO_INCREMENT,\n  `gid` varchar(128) NOT NULL COMMENT 'global transaction id',\n  `trans_type` varchar(45) not null COMMENT 'transaction type: saga | xa | tcc | msg',\n  `status` varchar(12) NOT NULL COMMENT 'transaction status: prepared | submitted | aborting | succeed | failed',\n  `query_prepared` varchar(1024) NOT NULL COMMENT 'url to check for msg|workflow',\n  `protocol` varchar(45) not null comment 'protocol: http | grpc | json-rpc',\n  `create_time` datetime DEFAULT NULL,\n  `update_time` datetime DEFAULT NULL,\n  `finish_time` datetime DEFAULT NULL,\n  `rollback_time` datetime DEFAULT NULL,\n  `options` varchar(1024) DEFAULT '' COMMENT 'options for transaction like: TimeoutToFail, RequestTimeout',\n  `custom_data` varchar(1024) DEFAULT '' COMMENT 'custom data for transaction',\n  `next_cron_interval` int(11) default null comment 'next cron interval. for use of cron job',\n  `next_cron_time` datetime default null comment 'next time to process this trans. for use of cron job',\n  `owner` varchar(128) not null default '' comment 'who is locking this trans',\n  `ext_data` TEXT comment 'extra data for this trans. currently used in workflow pattern',\n  `result` varchar(1024) DEFAULT '' COMMENT 'result for transaction',\n  `rollback_reason` varchar(1024) DEFAULT '' COMMENT 'rollback reason for transaction',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `gid` (`gid`),\n  key `owner`(`owner`),\n  key `status_next_cron_time` (`status`, `next_cron_time`) comment 'cron job will use this index to query trans'\n) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;\ndrop table IF EXISTS dtm.trans_branch_op;\nCREATE TABLE IF NOT EXISTS dtm.trans_branch_op (\n  `id` bigint(22) NOT NULL AUTO_INCREMENT,\n  `gid` varchar(128) NOT NULL COMMENT 'global transaction id',\n  `url` varchar(1024) NOT NULL COMMENT 'the url of this op',\n  `data` TEXT COMMENT 'request body, depreceated',\n  `bin_data` BLOB COMMENT 'request body',\n  `branch_id` VARCHAR(128) NOT NULL COMMENT 'transaction branch ID',\n  `op` varchar(45) NOT NULL COMMENT 'transaction operation type like: action | compensate | try | confirm | cancel',\n  `status` varchar(45) NOT NULL COMMENT 'transaction op status: prepared | succeed | failed',\n  `finish_time` datetime DEFAULT NULL,\n  `rollback_time` datetime DEFAULT NULL,\n  `create_time` datetime DEFAULT NULL,\n  `update_time` datetime DEFAULT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `gid_uniq` (`gid`, `branch_id`, `op`)\n) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;\ndrop table IF EXISTS dtm.kv;\nCREATE TABLE IF NOT EXISTS dtm.kv (\n  `id` bigint(22) NOT NULL AUTO_INCREMENT,\n  `cat` varchar(45) NOT NULL COMMENT 'the category of this data',\n  `k` varchar(128) NOT NULL,\n  `v` TEXT,\n  `version` bigint(22) default 1 COMMENT 'version of the value',\n  create_time datetime default NULL,\n  update_time datetime DEFAULT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE key `uniq_k`(`cat`, `k`)\n) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;\n"
  },
  {
    "path": "test/api_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage/registry\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestAPIVersion(t *testing.T) {\n\tresp, err := dtmcli.GetRestyClient().R().Get(dtmutil.DefaultHTTPServer + \"/version\")\n\tassert.Nil(t, err)\n\tassert.Equal(t, 200, resp.StatusCode())\n}\n\nfunc TestAPIQuery(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\terr := genMsg(gid).Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(gid)\n\tresp, err := dtmcli.GetRestyClient().R().SetQueryParam(\"gid\", gid).Get(dtmutil.DefaultHTTPServer + \"/query\")\n\tassert.Nil(t, err)\n\tm := map[string]interface{}{}\n\tassert.Equal(t, resp.StatusCode(), 200)\n\tdtmimp.MustUnmarshalString(resp.String(), &m)\n\tassert.NotEqual(t, nil, m[\"transaction\"])\n\tassert.Equal(t, 2, len(m[\"branches\"].([]interface{})))\n\n\tresp, err = dtmcli.GetRestyClient().R().SetQueryParam(\"gid\", \"\").Get(dtmutil.DefaultHTTPServer + \"/query\")\n\te2p(err)\n\tassert.Equal(t, resp.StatusCode(), 500)\n\n\tresp, err = dtmcli.GetRestyClient().R().SetQueryParam(\"gid\", \"1\").Get(dtmutil.DefaultHTTPServer + \"/query\")\n\te2p(err)\n\tassert.Equal(t, resp.StatusCode(), 200)\n\tdtmimp.MustUnmarshalString(resp.String(), &m)\n\tassert.Equal(t, nil, m[\"transaction\"])\n\tassert.Equal(t, 0, len(m[\"branches\"].([]interface{})))\n}\n\nfunc TestAPIAll(t *testing.T) {\n\tstartTime := time.Now()\n\tfor i := 0; i < 3; i++ { // add three\n\t\tgid := dtmimp.GetFuncName() + fmt.Sprintf(\"%d\", i)\n\t\terr := genMsg(gid).Submit()\n\t\tassert.Nil(t, err)\n\t\twaitTransProcessed(gid)\n\t}\n\tendTime := time.Now()\n\n\tresp, err := dtmcli.GetRestyClient().R().SetQueryParam(\"limit\", \"1\").Get(dtmutil.DefaultHTTPServer + \"/all\")\n\tassert.Nil(t, err)\n\tm := map[string]interface{}{}\n\tdtmimp.MustUnmarshalString(resp.String(), &m)\n\tnextPos := m[\"next_position\"].(string)\n\tassert.NotEqual(t, \"\", nextPos)\n\t// assert.Equal(t, 1, len(m[\"transactions\"].([]interface{})))\n\n\tresp, err = dtmcli.GetRestyClient().R().SetQueryParam(\"gid\", dtmimp.GetFuncName()+\"1\").Get(dtmutil.DefaultHTTPServer + \"/all\")\n\tassert.Nil(t, err)\n\tm = map[string]interface{}{}\n\tdtmimp.MustUnmarshalString(resp.String(), &m)\n\tassert.Equal(t, 1, len(m[\"transactions\"].([]interface{})))\n\n\tresp, err = dtmcli.GetRestyClient().R().SetQueryParams(map[string]string{\n\t\t\"limit\":    \"1\",\n\t\t\"position\": nextPos,\n\t}).Get(dtmutil.DefaultHTTPServer + \"/all\")\n\tassert.Nil(t, err)\n\tdtmimp.MustUnmarshalString(resp.String(), &m)\n\tnextPos2 := m[\"next_position\"].(string)\n\tassert.NotEqual(t, \"\", nextPos2)\n\tassert.NotEqual(t, nextPos, nextPos2)\n\t// assert.Equal(t, 1, len(m[\"transactions\"].([]interface{})))\n\n\tresp, err = dtmcli.GetRestyClient().R().SetQueryParams(map[string]string{\n\t\t\"limit\":    \"1000\",\n\t\t\"position\": nextPos,\n\t}).Get(dtmutil.DefaultHTTPServer + \"/all\")\n\tassert.Nil(t, err)\n\tdtmimp.MustUnmarshalString(resp.String(), &m)\n\tnextPos3 := m[\"next_position\"].(string)\n\tassert.Equal(t, \"\", nextPos3)\n\t// assert.Equal(t, 2, len(m[\"transactions\"].([]interface{}))) // the left 2.\n\n\t// filter test\n\tresp, err = dtmcli.GetRestyClient().R().SetQueryParams(map[string]string{\n\t\t\"limit\":           \"10\",\n\t\t\"status\":          \"succeed\",\n\t\t\"transType\":       \"msg\",\n\t\t\"createTimeStart\": strconv.Itoa(int(startTime.Add(time.Minute*-1).Unix() * 1000)),\n\t\t\"createTimeEnd\":   strconv.Itoa(int(endTime.Add(time.Minute*1).Unix() * 1000)),\n\t}).Get(dtmutil.DefaultHTTPServer + \"/all\")\n\tassert.Nil(t, err)\n\tdtmimp.MustUnmarshalString(resp.String(), &m)\n\tnextPos1 := m[\"next_position\"].(string)\n\t// assert.Equal(t, 3, len(m[\"transactions\"].([]interface{})))\n\tassert.GreaterOrEqual(t, len(m[\"transactions\"].([]interface{})), 3) // Be disturbed by something else test case, so use >=3 instead of =3.\n\tassert.Empty(t, nextPos1)                                           // is  over\n\tfor _, item := range m[\"transactions\"].([]interface{}) {\n\t\tg := item.(map[string]interface{})\n\t\tassert.Equal(t, \"msg\", g[\"trans_type\"])\n\t\tassert.Equal(t, \"succeed\", g[\"status\"])\n\t}\n\n\t// filter, five minutes ago\n\tresp, err = dtmcli.GetRestyClient().R().SetQueryParams(map[string]string{\n\t\t\"limit\":           \"10\",\n\t\t\"status\":          \"succeed\",\n\t\t\"transType\":       \"msg\",\n\t\t\"createTimeStart\": strconv.Itoa(int(startTime.Add(time.Minute*-10).Unix() * 1000)),\n\t\t\"createTimeEnd\":   strconv.Itoa(int(endTime.Add(time.Minute*-5).Unix() * 1000)),\n\t}).Get(dtmutil.DefaultHTTPServer + \"/all\")\n\tassert.Nil(t, err)\n\tdtmimp.MustUnmarshalString(resp.String(), &m)\n\tnextPos1 = m[\"next_position\"].(string)\n\tassert.Equal(t, 0, len(m[\"transactions\"].([]interface{})))\n\tassert.Empty(t, nextPos1) // is  over\n\n\t//fmt.Printf(\"pos1:%s,pos2:%s,pos3:%s\", nextPos, nextPos2, nextPos3)\n}\n\nfunc TestAPIScanKV(t *testing.T) {\n\tfor i := 0; i < 3; i++ { // add three\n\t\tassert.Nil(t, httpSubscribe(\"test_topic\"+fmt.Sprintf(\"%d\", i), \"http://dtm/test1\"))\n\t}\n\tresp, err := dtmcli.GetRestyClient().R().SetQueryParams(map[string]string{\n\t\t\"cat\":   \"topics\",\n\t\t\"limit\": \"1\",\n\t}).Get(dtmutil.DefaultHTTPServer + \"/scanKV\")\n\tassert.Nil(t, err)\n\tm := map[string]interface{}{}\n\tdtmimp.MustUnmarshalString(resp.String(), &m)\n\tnextPos := m[\"next_position\"].(string)\n\tassert.NotEqual(t, \"\", nextPos)\n\t// assert.Equal(t, 1, len(m[\"kv\"].([]interface{})))\n\n\tresp, err = dtmcli.GetRestyClient().R().SetQueryParams(map[string]string{\n\t\t\"cat\":      \"topics\",\n\t\t\"limit\":    \"1\",\n\t\t\"position\": nextPos,\n\t}).Get(dtmutil.DefaultHTTPServer + \"/scanKV\")\n\tassert.Nil(t, err)\n\tdtmimp.MustUnmarshalString(resp.String(), &m)\n\tnextPos2 := m[\"next_position\"].(string)\n\tassert.NotEqual(t, \"\", nextPos2)\n\tassert.NotEqual(t, nextPos, nextPos2)\n\t// assert.Equal(t, 1, len(m[\"kv\"].([]interface{})))\n\n\tresp, err = dtmcli.GetRestyClient().R().SetQueryParams(map[string]string{\n\t\t\"cat\":      \"topics\",\n\t\t\"limit\":    \"1000\",\n\t\t\"position\": nextPos,\n\t}).Get(dtmutil.DefaultHTTPServer + \"/scanKV\")\n\tassert.Nil(t, err)\n\tdtmimp.MustUnmarshalString(resp.String(), &m)\n\tnextPos3 := m[\"next_position\"].(string)\n\tassert.Equal(t, \"\", nextPos3)\n\t// assert.Equal(t, 2, len(m[\"kv\"].([]interface{}))) // the left 2.\n}\n\nfunc TestAPIQueryKV(t *testing.T) {\n\tm := map[string]interface{}{}\n\t// normal\n\tassert.Nil(t, httpSubscribe(\"test_topic_TestAPIQueryKV\", \"http://dtm/test1\"))\n\tresp, err := dtmcli.GetRestyClient().R().SetQueryParams(map[string]string{\n\t\t\"cat\": \"topics\",\n\t\t\"key\": \"test_topic_TestAPIQueryKV\",\n\t}).Get(dtmutil.DefaultHTTPServer + \"/queryKV\")\n\tassert.Nil(t, err)\n\tdtmimp.MustUnmarshalString(resp.String(), &m)\n\tassert.Equal(t, 1, len(m[\"kv\"].([]interface{})))\n\n\t// query non_existent topic\n\tresp, err = dtmcli.GetRestyClient().R().SetQueryParams(map[string]string{\n\t\t\"cat\": \"topics\",\n\t\t\"key\": \"non_existent_topic_TestAPIQueryKV\",\n\t}).Get(dtmutil.DefaultHTTPServer + \"/queryKV\")\n\tassert.Nil(t, err)\n\tdtmimp.MustUnmarshalString(resp.String(), &m)\n\tassert.Equal(t, 0, len(m[\"kv\"].([]interface{})))\n}\n\nfunc TestDtmMetrics(t *testing.T) {\n\trest, err := dtmcli.GetRestyClient().R().Get(\"http://localhost:36789/api/metrics\")\n\tassert.Nil(t, err)\n\tassert.Equal(t, rest.StatusCode(), 200)\n}\n\nfunc TestAPIResetCronTime(t *testing.T) {\n\ttestStoreResetCronTime(t, dtmimp.GetFuncName(), func(timeout int64, limit int64) (int64, bool, error) {\n\t\tsTimeout := strconv.FormatInt(timeout, 10)\n\t\tsLimit := strconv.FormatInt(limit, 10)\n\n\t\tresp, err := dtmcli.GetRestyClient().R().SetQueryParams(map[string]string{\n\t\t\t\"timeout\": sTimeout,\n\t\t\t\"limit\":   sLimit,\n\t\t}).Get(dtmutil.DefaultHTTPServer + \"/resetCronTime\")\n\n\t\tm := map[string]interface{}{}\n\t\tdtmimp.MustUnmarshalString(resp.String(), &m)\n\t\thasRemaining, ok := m[\"has_remaining\"].(bool)\n\t\tassert.Equal(t, ok, true)\n\t\tsucceedCount, ok := m[\"succeed_count\"].(float64)\n\t\tassert.Equal(t, ok, true)\n\t\treturn int64(succeedCount), hasRemaining, err\n\t})\n}\n\nfunc TestAPIForceStoppedNormal(t *testing.T) {\n\tsaga := genSaga(dtmimp.GetFuncName(), false, false)\n\tbusi.MainSwitch.TransOutResult.SetOnce(\"ONGOING\")\n\tsaga.Submit()\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, StatusSubmitted, getTransStatus(saga.Gid))\n\n\tresp, err := dtmcli.GetRestyClient().R().SetBody(map[string]string{\n\t\t\"gid\": saga.Gid,\n\t}).Post(dtmutil.DefaultHTTPServer + \"/forceStop\")\n\tassert.Nil(t, err)\n\tassert.Equal(t, resp.StatusCode(), http.StatusOK)\n\tassert.Equal(t, StatusFailed, getTransStatus(saga.Gid))\n}\n\nfunc TestAPIForceStoppedAbnormal(t *testing.T) {\n\tsaga := genSaga(dtmimp.GetFuncName(), false, false)\n\tsaga.Submit()\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n\n\tresp, err := dtmcli.GetRestyClient().R().SetBody(map[string]string{\n\t\t\"gid\": saga.Gid,\n\t}).Post(dtmutil.DefaultHTTPServer + \"/forceStop\")\n\tassert.Nil(t, err)\n\tassert.Equal(t, resp.StatusCode(), http.StatusConflict)\n}\n\nfunc TestAPIResetNextCronTime(t *testing.T) {\n\tsaga := genSaga(dtmimp.GetFuncName(), false, false)\n\tsaga.Submit()\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n\tgid := saga.Gid\n\n\ts := registry.GetStore()\n\tg := s.FindTransGlobalStore(saga.Gid)\n\n\t// reset\n\tresp, err := dtmcli.GetRestyClient().R().SetBody(map[string]string{\n\t\t\"gid\": saga.Gid,\n\t}).Post(dtmutil.DefaultHTTPServer + \"/resetNextCronTime\")\n\tassert.Nil(t, err)\n\tassert.Equal(t, http.StatusOK, resp.StatusCode())\n\n\t// after reset assert\n\tg2 := s.FindTransGlobalStore(gid)\n\tassert.NotNil(t, g2)\n\tassert.Equal(t, gid, g2.Gid)\n\tassert.Greater(t, time.Now().Add(3*time.Second), *g2.NextCronTime)\n\tassert.NotEqual(t, g.NextCronTime, g2.NextCronTime)\n}\n"
  },
  {
    "path": "test/base_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\n// BarrierModel barrier model for gorm\ntype BarrierModel struct {\n\tdtmutil.ModelBase\n\tdtmcli.BranchBarrier\n}\n\n// TableName gorm table name\nfunc (BarrierModel) TableName() string { return dtmimp.BarrierTableName }\n\nfunc TestBaseSqlDB(t *testing.T) {\n\tasserts := assert.New(t)\n\tdb := dtmutil.DbGet(busi.BusiConf)\n\tbarrier := &dtmcli.BranchBarrier{\n\t\tTransType: \"saga\",\n\t\tGid:       \"gid2\",\n\t\tBranchID:  \"branch_id2\",\n\t\tOp:        dtmimp.OpAction,\n\t\tBarrierID: 1,\n\t}\n\tdb.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))\n\ttx, err := db.ToSQLDB().Begin()\n\tasserts.Nil(err)\n\terr = barrier.Call(tx, func(tx *sql.Tx) error {\n\t\tlogger.Debugf(\"rollback gid2\")\n\t\treturn fmt.Errorf(\"gid2 error\")\n\t})\n\tasserts.Error(err, fmt.Errorf(\"gid2 error\"))\n\tdbr := db.Model(&BarrierModel{}).Where(\"gid=?\", \"gid1\").Find(&[]BarrierModel{})\n\tasserts.Equal(dbr.RowsAffected, int64(1))\n\tdbr = db.Model(&BarrierModel{}).Where(\"gid=?\", \"gid2\").Find(&[]BarrierModel{})\n\tasserts.Equal(dbr.RowsAffected, int64(0))\n\tbarrier.BarrierID = 0\n\terr = barrier.CallWithDB(db.ToSQLDB(), func(tx *sql.Tx) error {\n\t\tlogger.Debugf(\"submit gid2\")\n\t\treturn nil\n\t})\n\tasserts.Nil(err)\n\tdbr = db.Model(&BarrierModel{}).Where(\"gid=?\", \"gid2\").Find(&[]BarrierModel{})\n\tasserts.Equal(dbr.RowsAffected, int64(1))\n}\n\nfunc TestBaseHttp(t *testing.T) {\n\tresp, err := dtmcli.GetRestyClient().R().SetQueryParam(\"panic_string\", \"1\").Post(busi.Busi + \"/TestPanic\")\n\tassert.Nil(t, err)\n\tassert.Contains(t, resp.String(), \"panic_string\")\n\tresp, err = dtmcli.GetRestyClient().R().SetQueryParam(\"panic_error\", \"1\").Post(busi.Busi + \"/TestPanic\")\n\tassert.Nil(t, err)\n\tassert.Contains(t, resp.String(), \"panic_error\")\n}\n"
  },
  {
    "path": "test/busi/barrier.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage busi\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/gin-gonic/gin\"\n\t\"go.mongodb.org/mongo-driver/mongo\"\n\temptypb \"google.golang.org/protobuf/types/known/emptypb\"\n)\n\nfunc init() {\n\tsetupFuncs[\"BarrierSetup\"] = func(app *gin.Engine) {\n\t\tapp.POST(BusiAPI+\"/SagaBTransIn\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\tbarrier := MustBarrierFromGin(c)\n\t\t\treturn barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {\n\t\t\t\treturn SagaAdjustBalance(tx, TransInUID, reqFrom(c).Amount, reqFrom(c).TransInResult)\n\t\t\t})\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/SagaBTransInCom\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\tbarrier := MustBarrierFromGin(c)\n\t\t\treturn barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {\n\t\t\t\treturn SagaAdjustBalance(tx, TransInUID, -reqFrom(c).Amount, \"\")\n\t\t\t})\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/SagaB2TransIn\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\tbarrier := MustBarrierFromGin(c)\n\t\t\terr := barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {\n\t\t\t\treturn SagaAdjustBalance(tx, TransInUID, reqFrom(c).Amount/2, reqFrom(c).TransInResult)\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {\n\t\t\t\treturn SagaAdjustBalance(tx, TransInUID, reqFrom(c).Amount/2, reqFrom(c).TransInResult)\n\t\t\t})\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/SagaB2TransInCom\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\tbarrier := MustBarrierFromGin(c)\n\t\t\terr := barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {\n\t\t\t\treturn SagaAdjustBalance(tx, TransInUID, -reqFrom(c).Amount/2, \"\")\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {\n\t\t\t\treturn SagaAdjustBalance(tx, TransInUID, -reqFrom(c).Amount, \"\")\n\t\t\t})\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/SagaBTransOut\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\tbarrier := MustBarrierFromGin(c)\n\t\t\treturn barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {\n\t\t\t\treturn SagaAdjustBalance(tx, TransOutUID, -reqFrom(c).Amount, reqFrom(c).TransOutResult)\n\t\t\t})\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/SagaBTransOutCom\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\tbarrier := MustBarrierFromGin(c)\n\t\t\treturn barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {\n\t\t\t\treturn SagaAdjustBalance(tx, TransOutUID, reqFrom(c).Amount, \"\")\n\t\t\t})\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/SagaBTransOutGorm\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\treq := reqFrom(c)\n\t\t\tbarrier := MustBarrierFromGin(c)\n\t\t\ttx := dbGet().DB.Begin()\n\t\t\treturn barrier.Call(tx.Statement.ConnPool.(*sql.Tx), func(tx1 *sql.Tx) error {\n\t\t\t\treturn tx.Exec(\"update dtm_busi.user_account set balance = balance + ? where user_id = ?\", -req.Amount, TransOutUID).Error\n\t\t\t})\n\t\t}))\n\n\t\tapp.POST(BusiAPI+\"/TccBTransInTry\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\treq := reqFrom(c)\n\t\t\tif req.TransInResult != \"\" {\n\t\t\t\treturn string2DtmError(req.TransInResult)\n\t\t\t}\n\t\t\treturn MustBarrierFromGin(c).CallWithDB(pdbGet(), func(tx *sql.Tx) error {\n\t\t\t\treturn tccAdjustTrading(tx, TransInUID, req.Amount)\n\t\t\t})\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/TccBTransInConfirm\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\treturn MustBarrierFromGin(c).CallWithDB(pdbGet(), func(tx *sql.Tx) error {\n\t\t\t\treturn tccAdjustBalance(tx, TransInUID, reqFrom(c).Amount)\n\t\t\t})\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/TccBTransInCancel\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\treturn MustBarrierFromGin(c).CallWithDB(pdbGet(), func(tx *sql.Tx) error {\n\t\t\t\treturn tccAdjustTrading(tx, TransInUID, -reqFrom(c).Amount)\n\t\t\t})\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/SagaMultiSource\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\tbarrier := MustBarrierFromGin(c)\n\t\t\ttransOutSource := pdbGet()\n\t\t\terr := barrier.CallWithDB(transOutSource, func(tx *sql.Tx) error {\n\t\t\t\treturn SagaAdjustBalance(tx, TransOutUID, -reqFrom(c).Amount, reqFrom(c).TransOutResult)\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\ttransInSource := pdbGet()\n\t\t\treturn barrier.CallWithDB(transInSource, func(tx *sql.Tx) error {\n\t\t\t\treturn SagaAdjustBalance(tx, TransInUID, reqFrom(c).Amount, reqFrom(c).TransInResult)\n\t\t\t})\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/SagaMultiSourceRevert\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\tbarrier := MustBarrierFromGin(c)\n\t\t\ttransOutSource := pdbGet()\n\t\t\terr := barrier.CallWithDB(transOutSource, func(tx *sql.Tx) error {\n\t\t\t\treturn SagaAdjustBalance(tx, TransOutUID, +reqFrom(c).Amount, \"\")\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\ttransInSource := pdbGet()\n\t\t\treturn barrier.CallWithDB(transInSource, func(tx *sql.Tx) error {\n\t\t\t\treturn SagaAdjustBalance(tx, TransInUID, -reqFrom(c).Amount, \"\")\n\t\t\t})\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/SagaRedisTransIn\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\treturn MustBarrierFromGin(c).RedisCheckAdjustAmount(RedisGet(), GetRedisAccountKey(TransInUID), reqFrom(c).Amount, 7*86400)\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/SagaRedisTransInCom\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\treturn MustBarrierFromGin(c).RedisCheckAdjustAmount(RedisGet(), GetRedisAccountKey(TransInUID), -reqFrom(c).Amount, 7*86400)\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/SagaRedisTransOut\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\treturn MustBarrierFromGin(c).RedisCheckAdjustAmount(RedisGet(), GetRedisAccountKey(TransOutUID), -reqFrom(c).Amount, 7*86400)\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/SagaRedisTransOutCom\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\treturn MustBarrierFromGin(c).RedisCheckAdjustAmount(RedisGet(), GetRedisAccountKey(TransOutUID), reqFrom(c).Amount, 7*86400)\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/SagaMongoTransIn\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\treturn MustBarrierFromGin(c).MongoCall(MongoGet(), func(sc mongo.SessionContext) error {\n\t\t\t\treturn SagaMongoAdjustBalance(sc, sc.Client(), TransInUID, reqFrom(c).Amount, reqFrom(c).TransInResult)\n\t\t\t})\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/SagaMongoTransInCom\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\treturn MustBarrierFromGin(c).MongoCall(MongoGet(), func(sc mongo.SessionContext) error {\n\t\t\t\treturn SagaMongoAdjustBalance(sc, sc.Client(), TransInUID, -reqFrom(c).Amount, \"\")\n\t\t\t})\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/SagaMongoTransOut\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\treturn MustBarrierFromGin(c).MongoCall(MongoGet(), func(sc mongo.SessionContext) error {\n\t\t\t\treturn SagaMongoAdjustBalance(sc, sc.Client(), TransOutUID, -reqFrom(c).Amount, reqFrom(c).TransOutResult)\n\t\t\t})\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/SagaMongoTransOutCom\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\treturn MustBarrierFromGin(c).MongoCall(MongoGet(), func(sc mongo.SessionContext) error {\n\t\t\t\treturn SagaMongoAdjustBalance(sc, sc.Client(), TransOutUID, reqFrom(c).Amount, \"\")\n\t\t\t})\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/TccBTransOutTry\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\treq := reqFrom(c)\n\t\t\tif req.TransOutResult != \"\" {\n\t\t\t\treturn string2DtmError(req.TransOutResult)\n\t\t\t}\n\t\t\tbb := MustBarrierFromGin(c)\n\t\t\tif req.Store == Redis {\n\t\t\t\treturn bb.RedisCheckAdjustAmount(RedisGet(), GetRedisAccountKey(TransOutUID), req.Amount, 7*86400)\n\t\t\t} else if req.Store == Mongo {\n\t\t\t\treturn bb.MongoCall(MongoGet(), func(sc mongo.SessionContext) error {\n\t\t\t\t\treturn SagaMongoAdjustBalance(sc, sc.Client(), TransOutUID, -req.Amount, \"\")\n\t\t\t\t})\n\t\t\t}\n\n\t\t\treturn bb.CallWithDB(pdbGet(), func(tx *sql.Tx) error {\n\t\t\t\treturn tccAdjustTrading(tx, TransOutUID, -req.Amount)\n\t\t\t})\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/TccBTransOutConfirm\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\t\tif reqFrom(c).Store == Redis || reqFrom(c).Store == Mongo {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn MustBarrierFromGin(c).CallWithDB(pdbGet(), func(tx *sql.Tx) error {\n\t\t\t\treturn tccAdjustBalance(tx, TransOutUID, -reqFrom(c).Amount)\n\t\t\t})\n\t\t}))\n\t\tapp.POST(BusiAPI+\"/TccBTransOutCancel\", dtmutil.WrapHandler(TccBarrierTransOutCancel))\n\t}\n}\n\n// TccBarrierTransOutCancel will be use in test\nfunc TccBarrierTransOutCancel(c *gin.Context) interface{} {\n\treq := reqFrom(c)\n\tbb := MustBarrierFromGin(c)\n\tif req.Store == Redis {\n\t\treturn bb.RedisCheckAdjustAmount(RedisGet(), GetRedisAccountKey(TransOutUID), -req.Amount, 7*86400)\n\t}\n\tif req.Store == Mongo {\n\t\treturn bb.MongoCall(MongoGet(), func(sc mongo.SessionContext) error {\n\t\t\treturn SagaMongoAdjustBalance(sc, sc.Client(), TransOutUID, reqFrom(c).Amount, \"\")\n\t\t})\n\t}\n\treturn bb.CallWithDB(pdbGet(), func(tx *sql.Tx) error {\n\t\treturn tccAdjustTrading(tx, TransOutUID, reqFrom(c).Amount)\n\t})\n}\n\nfunc (s *busiServer) TransInBSaga(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\tbarrier := MustBarrierFromGrpc(ctx)\n\treturn &emptypb.Empty{}, barrier.Call(txGet(), func(tx *sql.Tx) error {\n\t\treturn sagaGrpcAdjustBalance(tx, TransInUID, in.Amount, in.TransInResult)\n\t})\n}\n\nfunc (s *busiServer) TransOutBSaga(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\tbarrier := MustBarrierFromGrpc(ctx)\n\treturn &emptypb.Empty{}, barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {\n\t\treturn sagaGrpcAdjustBalance(tx, TransOutUID, -in.Amount, in.TransOutResult)\n\t})\n}\n\nfunc (s *busiServer) TransInRevertBSaga(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\tbarrier := MustBarrierFromGrpc(ctx)\n\treturn &emptypb.Empty{}, barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {\n\t\treturn sagaGrpcAdjustBalance(tx, TransInUID, -in.Amount, \"\")\n\t})\n}\n\nfunc (s *busiServer) TransOutRevertBSaga(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\tbarrier := MustBarrierFromGrpc(ctx)\n\treturn &emptypb.Empty{}, barrier.CallWithDB(pdbGet(), func(tx *sql.Tx) error {\n\t\treturn sagaGrpcAdjustBalance(tx, TransOutUID, in.Amount, \"\")\n\t})\n}\n\nfunc (s *busiServer) TransInRedis(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\tbarrier := MustBarrierFromGrpc(ctx)\n\treturn &emptypb.Empty{}, barrier.RedisCheckAdjustAmount(RedisGet(), GetRedisAccountKey(TransInUID), int(in.Amount), 86400)\n}\n\nfunc (s *busiServer) TransOutRedis(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\tbarrier := MustBarrierFromGrpc(ctx)\n\treturn &emptypb.Empty{}, barrier.RedisCheckAdjustAmount(RedisGet(), GetRedisAccountKey(TransOutUID), int(-in.Amount), 86400)\n}\n\nfunc (s *busiServer) TransInRevertRedis(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\tbarrier := MustBarrierFromGrpc(ctx)\n\treturn &emptypb.Empty{}, barrier.RedisCheckAdjustAmount(RedisGet(), GetRedisAccountKey(TransInUID), -int(in.Amount), 86400)\n}\n\nfunc (s *busiServer) TransOutRevertRedis(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\tbarrier := MustBarrierFromGrpc(ctx)\n\treturn &emptypb.Empty{}, barrier.RedisCheckAdjustAmount(RedisGet(), GetRedisAccountKey(TransOutUID), int(in.Amount), 86400)\n}\n\nfunc (s *busiServer) QueryPreparedB(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\tbarrier := MustBarrierFromGrpc(ctx)\n\terr := barrier.QueryPrepared(dbGet().ToSQLDB())\n\treturn &emptypb.Empty{}, dtmgrpc.DtmError2GrpcError(err)\n}\n\nfunc (s *busiServer) QueryPreparedRedis(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\tbarrier := MustBarrierFromGrpc(ctx)\n\terr := barrier.RedisQueryPrepared(RedisGet(), 86400)\n\treturn &emptypb.Empty{}, dtmgrpc.DtmError2GrpcError(err)\n}\n"
  },
  {
    "path": "test/busi/base_grpc.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage busi\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/logger\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgpb\"\n\t\"github.com/dtm-labs/dtm/client/workflow\"\n\tgrpc \"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/credentials/insecure\"\n\tstatus \"google.golang.org/grpc/status\"\n\temptypb \"google.golang.org/protobuf/types/known/emptypb\"\n)\n\n// BusiGrpc busi service grpc address\nvar BusiGrpc = fmt.Sprintf(\"localhost:%d\", BusiGrpcPort)\n\n// DtmClient grpc client for dtm\nvar DtmClient dtmgpb.DtmClient\n\n// BusiCli grpc client for busi\nvar BusiCli BusiClient\n\nfunc retry(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {\n\tlogger.Debugf(\"in retry interceptor\")\n\terr := invoker(ctx, method, req, reply, cc, opts...)\n\tif st, _ := status.FromError(err); st != nil && st.Code() == codes.Unavailable {\n\t\tlogger.Errorf(\"invoker return err: %v\", err)\n\t\ttime.Sleep(1000 * time.Millisecond)\n\t\terr = invoker(ctx, method, req, reply, cc, opts...)\n\t}\n\treturn err\n}\n\n// GrpcStartup for grpc\nfunc GrpcStartup() *grpc.Server {\n\tconn, err := grpc.Dial(dtmutil.DefaultGrpcServer, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithUnaryInterceptor(dtmgimp.GrpcClientLog))\n\tlogger.FatalIfError(err)\n\tDtmClient = dtmgpb.NewDtmClient(conn)\n\tlogger.Debugf(\"dtm client inited\")\n\t// in github actions, the call is failed sometime, so add a retry\n\tconn1, err := grpc.Dial(BusiGrpc, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithChainUnaryInterceptor(workflow.Interceptor, retry))\n\tlogger.FatalIfError(err)\n\tBusiCli = NewBusiClient(conn1)\n\n\ts := grpc.NewServer(grpc.UnaryInterceptor(dtmgimp.GrpcServerLog))\n\tRegisterBusiServer(s, &busiServer{})\n\treturn s\n}\n\n// RunGrpc start to serve grpc\nfunc RunGrpc(server *grpc.Server) {\n\tlis, err := net.Listen(\"tcp\", fmt.Sprintf(\":%d\", BusiGrpcPort))\n\tlogger.FatalIfError(err)\n\tlogger.Debugf(\"busi grpc listening at %v\", lis.Addr())\n\terr = server.Serve(lis)\n\tlogger.Errorf(\"grpc server serve return: %v\", err)\n\tlogger.FatalIfError(err)\n}\n\n// busiServer is used to implement busi.BusiServer.\ntype busiServer struct {\n\tUnimplementedBusiServer\n}\n\nfunc (s *busiServer) QueryPrepared(ctx context.Context, in *ReqGrpc) (*BusiReply, error) {\n\tres := MainSwitch.QueryPreparedResult.Fetch()\n\terr := string2DtmError(res)\n\n\treturn &BusiReply{Message: \"a sample data\"}, dtmgrpc.DtmError2GrpcError(err)\n}\n\nfunc (s *busiServer) TransIn(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\treturn &emptypb.Empty{}, handleGrpcBusiness(in, MainSwitch.TransInResult.Fetch(), in.TransInResult, dtmimp.GetFuncName())\n}\n\nfunc (s *busiServer) TransOut(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\treturn &emptypb.Empty{}, handleGrpcBusiness(in, MainSwitch.TransOutResult.Fetch(), in.TransOutResult, dtmimp.GetFuncName())\n}\n\nfunc (s *busiServer) TransInRevert(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\treturn &emptypb.Empty{}, handleGrpcBusiness(in, MainSwitch.TransInRevertResult.Fetch(), \"\", dtmimp.GetFuncName())\n}\n\nfunc (s *busiServer) TransOutRevert(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\treturn &emptypb.Empty{}, handleGrpcBusiness(in, MainSwitch.TransOutRevertResult.Fetch(), \"\", dtmimp.GetFuncName())\n}\n\nfunc (s *busiServer) TransInConfirm(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\treturn &emptypb.Empty{}, handleGrpcBusiness(in, MainSwitch.TransInConfirmResult.Fetch(), \"\", dtmimp.GetFuncName())\n}\n\nfunc (s *busiServer) TransOutConfirm(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\treturn &emptypb.Empty{}, handleGrpcBusiness(in, MainSwitch.TransOutConfirmResult.Fetch(), \"\", dtmimp.GetFuncName())\n}\n\nfunc (s *busiServer) TransInTcc(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\treturn &emptypb.Empty{}, handleGrpcBusiness(in, MainSwitch.TransInResult.Fetch(), in.TransInResult, dtmimp.GetFuncName())\n}\n\nfunc (s *busiServer) TransOutTcc(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\treturn &emptypb.Empty{}, handleGrpcBusiness(in, MainSwitch.TransOutResult.Fetch(), in.TransOutResult, dtmimp.GetFuncName())\n}\n\nfunc (s *busiServer) TransInXa(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\treturn &emptypb.Empty{}, dtmgrpc.XaLocalTransaction(ctx, BusiConf, func(db *sql.DB, xa *dtmgrpc.XaGrpc) error {\n\t\treturn sagaGrpcAdjustBalance(db, TransInUID, in.Amount, in.TransInResult)\n\t})\n}\n\nfunc (s *busiServer) TransOutXa(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\treturn &emptypb.Empty{}, dtmgrpc.XaLocalTransaction(ctx, BusiConf, func(db *sql.DB, xa *dtmgrpc.XaGrpc) error {\n\t\treturn sagaGrpcAdjustBalance(db, TransOutUID, in.Amount, in.TransOutResult)\n\t})\n}\n\nfunc (s *busiServer) TransInTccNested(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\ttcc, err := dtmgrpc.TccFromGrpc(ctx)\n\tlogger.FatalIfError(err)\n\tr := &emptypb.Empty{}\n\terr = tcc.CallBranch(in, BusiGrpc+\"/busi.Busi/TransIn\", BusiGrpc+\"/busi.Busi/TransInConfirm\", BusiGrpc+\"/busi.Busi/TransInRevert\", r)\n\tlogger.FatalIfError(err)\n\treturn r, handleGrpcBusiness(in, MainSwitch.TransInResult.Fetch(), in.TransInResult, dtmimp.GetFuncName())\n}\n\nfunc (s *busiServer) TransOutHeaderYes(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\tmeta := dtmgimp.GetMetaFromContext(ctx, \"test_header\")\n\tif meta == \"\" {\n\t\treturn &emptypb.Empty{}, errors.New(\"no header found in HeaderYes\")\n\t}\n\treturn &emptypb.Empty{}, handleGrpcBusiness(in, MainSwitch.TransOutResult.Fetch(), in.TransOutResult, dtmimp.GetFuncName())\n}\n\nfunc (s *busiServer) TransOutHeaderNo(ctx context.Context, in *ReqGrpc) (*emptypb.Empty, error) {\n\tmeta := dtmgimp.GetMetaFromContext(ctx, \"test_header\")\n\tif meta != \"\" {\n\t\treturn &emptypb.Empty{}, errors.New(\"header found in HeaderNo\")\n\t}\n\treturn &emptypb.Empty{}, nil\n}\n"
  },
  {
    "path": "test/busi/base_http.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage busi\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"strings\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/workflow\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/gin-gonic/gin\"\n\t\"gorm.io/driver/mysql\"\n\t\"gorm.io/driver/postgres\"\n\t\"gorm.io/gorm\"\n)\n\nconst (\n\t// BusiAPI busi api prefix\n\tBusiAPI = \"/api/busi\"\n\t// BusiPort busi server port\n\tBusiPort = 8081\n\t// BusiGrpcPort busi server port\n\tBusiGrpcPort = 58081\n)\n\ntype setupFunc func(*gin.Engine)\n\nvar setupFuncs = map[string]setupFunc{}\n\n// Busi busi service url prefix\nvar Busi = fmt.Sprintf(\"http://localhost:%d%s\", BusiPort, BusiAPI)\n\n// SleepCancelHandler 1\ntype SleepCancelHandler func(c *gin.Context) interface{}\n\nvar sleepCancelHandler SleepCancelHandler\n\n// SetSleepCancelHandler 1\nfunc SetSleepCancelHandler(handler SleepCancelHandler) {\n\tsleepCancelHandler = handler\n}\n\n// WebHookResult 1\nvar WebHookResult gin.H\n\n// BaseAppStartup base app startup\nfunc BaseAppStartup() *gin.Engine {\n\tlogger.Infof(\"examples starting\")\n\tapp := dtmutil.GetGinApp()\n\tapp.Use(func(c *gin.Context) {\n\t\tv := MainSwitch.NextResult.Fetch()\n\t\tif v != \"\" {\n\t\t\tc.JSON(200, gin.H{\"dtm_result\": v})\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\tc.Next()\n\t})\n\n\tBaseAddRoute(app)\n\taddJrpcRoute(app)\n\tfor k, v := range setupFuncs {\n\t\tlogger.Debugf(\"initing %s\", k)\n\t\tv(app)\n\t}\n\treturn app\n}\n\n// RunHTTP will run http server\nfunc RunHTTP(app *gin.Engine) {\n\tlogger.Debugf(\"Starting busi at: %d\", BusiPort)\n\terr := app.Run(fmt.Sprintf(\":%d\", BusiPort))\n\tdtmimp.FatalIfError(err)\n}\n\n// BaseAddRoute add base route handler\nfunc BaseAddRoute(app *gin.Engine) {\n\tapp.POST(BusiAPI+\"/workflow/resume\", dtmutil.WrapHandler(func(ctx *gin.Context) interface{} {\n\t\tdata, err := ioutil.ReadAll(ctx.Request.Body)\n\t\tlogger.FatalIfError(err)\n\t\treturn workflow.ExecuteByQS(ctx.Request.URL.Query(), data)\n\t}))\n\tapp.POST(BusiAPI+\"/TransIn\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\treturn handleGeneralBusiness(c, MainSwitch.TransInResult.Fetch(), reqFrom(c).TransInResult, \"transIn\")\n\t}))\n\tapp.POST(BusiAPI+\"/TransOut\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\treturn handleGeneralBusiness(c, MainSwitch.TransOutResult.Fetch(), reqFrom(c).TransOutResult, \"TransOut\")\n\t}))\n\tapp.POST(BusiAPI+\"/TransInConfirm\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\treturn handleGeneralBusiness(c, MainSwitch.TransInConfirmResult.Fetch(), \"\", \"TransInConfirm\")\n\t}))\n\tapp.POST(BusiAPI+\"/TransOutConfirm\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\treturn handleGeneralBusiness(c, MainSwitch.TransOutConfirmResult.Fetch(), \"\", \"TransOutConfirm\")\n\t}))\n\tapp.POST(BusiAPI+\"/TransInRevert\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\treturn handleGeneralBusiness(c, MainSwitch.TransInRevertResult.Fetch(), \"\", \"TransInRevert\")\n\t}))\n\tapp.POST(BusiAPI+\"/TransOutRevert\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\treturn handleGeneralBusiness(c, MainSwitch.TransOutRevertResult.Fetch(), \"\", \"TransOutRevert\")\n\t}))\n\tapp.POST(BusiAPI+\"/TransInOld\", oldWrapHandler(func(c *gin.Context) (interface{}, error) {\n\t\treturn handleGeneralBusinessCompatible(c, MainSwitch.TransInResult.Fetch(), reqFrom(c).TransInResult, \"transIn\")\n\t}))\n\tapp.POST(BusiAPI+\"/TransOutOld\", oldWrapHandler(func(c *gin.Context) (interface{}, error) {\n\t\treturn handleGeneralBusinessCompatible(c, MainSwitch.TransOutResult.Fetch(), reqFrom(c).TransOutResult, \"TransOut\")\n\t}))\n\tapp.POST(BusiAPI+\"/TransInConfirmOld\", oldWrapHandler(func(c *gin.Context) (interface{}, error) {\n\t\treturn handleGeneralBusinessCompatible(c, MainSwitch.TransInConfirmResult.Fetch(), \"\", \"TransInConfirm\")\n\t}))\n\tapp.POST(BusiAPI+\"/TransOutConfirmOld\", oldWrapHandler(func(c *gin.Context) (interface{}, error) {\n\t\treturn handleGeneralBusinessCompatible(c, MainSwitch.TransOutConfirmResult.Fetch(), \"\", \"TransOutConfirm\")\n\t}))\n\tapp.POST(BusiAPI+\"/TransInRevertOld\", oldWrapHandler(func(c *gin.Context) (interface{}, error) {\n\t\treturn handleGeneralBusinessCompatible(c, MainSwitch.TransInRevertResult.Fetch(), \"\", \"TransInRevert\")\n\t}))\n\tapp.POST(BusiAPI+\"/TransOutRevertOld\", oldWrapHandler(func(c *gin.Context) (interface{}, error) {\n\t\treturn handleGeneralBusinessCompatible(c, MainSwitch.TransOutRevertResult.Fetch(), \"\", \"TransOutRevert\")\n\t}))\n\n\tapp.GET(BusiAPI+\"/QueryPrepared\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\tlogger.Debugf(\"%s QueryPrepared\", c.Query(\"gid\"))\n\t\treturn string2DtmError(dtmimp.OrString(MainSwitch.QueryPreparedResult.Fetch(), dtmcli.ResultSuccess))\n\t}))\n\tapp.GET(BusiAPI+\"/QueryPreparedB\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\tlogger.Debugf(\"%s QueryPreparedB\", c.Query(\"gid\"))\n\t\tbb := MustBarrierFromGin(c)\n\t\tdb := dbGet().ToSQLDB()\n\t\treturn bb.QueryPrepared(db)\n\t}))\n\tapp.GET(BusiAPI+\"/RedisQueryPrepared\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\tlogger.Debugf(\"%s RedisQueryPrepared\", c.Query(\"gid\"))\n\t\tbb := MustBarrierFromGin(c)\n\t\treturn bb.RedisQueryPrepared(RedisGet(), 86400)\n\t}))\n\tapp.GET(BusiAPI+\"/MongoQueryPrepared\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\tlogger.Debugf(\"%s MongoQueryPrepared\", c.Query(\"gid\"))\n\t\tbb := MustBarrierFromGin(c)\n\t\treturn bb.MongoQueryPrepared(MongoGet())\n\t}))\n\tapp.POST(BusiAPI+\"/TransInXa\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\treturn dtmcli.XaLocalTransaction(c.Request.URL.Query(), BusiConf, func(db *sql.DB, xa *dtmcli.Xa) error {\n\t\t\treturn SagaAdjustBalance(db, TransInUID, reqFrom(c).Amount, reqFrom(c).TransInResult)\n\t\t})\n\t}))\n\tapp.POST(BusiAPI+\"/TransOutXa\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\treturn dtmcli.XaLocalTransaction(c.Request.URL.Query(), BusiConf, func(db *sql.DB, xa *dtmcli.Xa) error {\n\t\t\treturn SagaAdjustBalance(db, TransOutUID, -reqFrom(c).Amount, reqFrom(c).TransOutResult)\n\t\t})\n\t}))\n\tapp.POST(BusiAPI+\"/TransOutTimeout\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\treturn handleGeneralBusiness(c, MainSwitch.TransOutResult.Fetch(), reqFrom(c).TransOutResult, \"TransOut\")\n\t}))\n\tapp.POST(BusiAPI+\"/TransInTccNested\", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {\n\t\ttcc, err := dtmcli.TccFromQuery(c.Request.URL.Query())\n\t\tlogger.FatalIfError(err)\n\t\tlogger.Debugf(\"TransInTccNested \")\n\t\tresp, err := tcc.CallBranch(&ReqHTTP{Amount: reqFrom(c).Amount}, Busi+\"/TransIn\", Busi+\"/TransInConfirm\", Busi+\"/TransInRevert\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn resp\n\t}))\n\tapp.POST(BusiAPI+\"/TransOutXaGorm\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\treturn dtmcli.XaLocalTransaction(c.Request.URL.Query(), BusiConf, func(db *sql.DB, xa *dtmcli.Xa) error {\n\t\t\tif reqFrom(c).TransOutResult == dtmcli.ResultFailure {\n\t\t\t\treturn dtmcli.ErrFailure\n\t\t\t}\n\t\t\tvar dia gorm.Dialector\n\t\t\tif dtmcli.GetCurrentDBType() == dtmcli.DBTypeMysql {\n\t\t\t\tdia = mysql.New(mysql.Config{Conn: db})\n\t\t\t} else if dtmcli.GetCurrentDBType() == dtmcli.DBTypePostgres {\n\t\t\t\tdia = postgres.New(postgres.Config{Conn: db})\n\t\t\t}\n\t\t\tgdb, err := gorm.Open(dia, &gorm.Config{})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdbr := gdb.Exec(\"update dtm_busi.user_account set balance=balance-? where user_id=?\", reqFrom(c).Amount, TransOutUID)\n\t\t\treturn dbr.Error\n\t\t})\n\t}))\n\n\tapp.POST(BusiAPI+\"/TestPanic\", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {\n\t\tif c.Query(\"panic_error\") != \"\" {\n\t\t\tpanic(errors.New(\"panic_error\"))\n\t\t} else if c.Query(\"panic_string\") != \"\" {\n\t\t\tpanic(\"panic_string\")\n\t\t}\n\t\treturn nil\n\t}))\n\tapp.POST(BusiAPI+\"/SleepCancel\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\treturn sleepCancelHandler(c)\n\t}))\n\tapp.POST(BusiAPI+\"/TransOutHeaderYes\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\th := c.GetHeader(\"test_header\")\n\t\tif h == \"\" {\n\t\t\treturn errors.New(\"no test_header found in TransOutHeaderYes\")\n\t\t}\n\t\treturn handleGeneralBusiness(c, MainSwitch.TransOutResult.Fetch(), reqFrom(c).TransOutResult, \"TransOut\")\n\t}))\n\tapp.POST(BusiAPI+\"/TransOutHeaderNo\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\th := c.GetHeader(\"test_header\")\n\t\tif h != \"\" {\n\t\t\treturn errors.New(\"test_header found in TransOutHeaderNo\")\n\t\t}\n\t\treturn nil\n\t}))\n\n\tretryNums := 3\n\tapp.POST(BusiAPI+\"/TransInRetry\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\tif retryNums != 0 {\n\t\t\tretryNums--\n\t\t\treturn fmt.Errorf((\"should be retried\"))\n\t\t}\n\t\tretryNums = 3\n\t\treturn nil\n\t}))\n\tapp.POST(BusiAPI+\"/AlertWebHook\", dtmutil.WrapHandler(func(ctx *gin.Context) interface{} {\n\t\terr := ctx.BindJSON(&WebHookResult)\n\t\tdtmimp.FatalIfError(err)\n\t\tif strings.Contains(WebHookResult[\"gid\"].(string), \"Error\") {\n\t\t\treturn errors.New(\"gid contains 'Error', so return error\")\n\t\t}\n\t\treturn nil\n\t}))\n}\n"
  },
  {
    "path": "test/busi/base_jrpc.go",
    "content": "package busi\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// BusiJrpcURL url prefix for busi\nvar BusiJrpcURL = fmt.Sprintf(\"http://localhost:%d/api/json-rpc?method=\", BusiPort)\n\nfunc addJrpcRoute(app *gin.Engine) {\n\tapp.POST(\"/api/json-rpc\", dtmutil.WrapHandler(func(c *gin.Context) interface{} {\n\t\tvar data map[string]interface{}\n\t\terr := c.BindJSON(&data)\n\t\tdtmimp.E2P(err)\n\t\tlogger.Debugf(\"method is: %s\", data[\"method\"])\n\t\tvar rerr map[string]interface{}\n\t\tr := MainSwitch.JrpcResult.Fetch()\n\t\tif r != \"\" {\n\t\t\trerr = map[string]interface{}{\n\t\t\t\t\"code\": map[string]int{\n\t\t\t\t\t\"FAILURE\": dtmimp.JrpcCodeFailure,\n\t\t\t\t\t\"ONGOING\": dtmimp.JrpcCodeOngoing,\n\t\t\t\t\t\"OTHER\":   -23977,\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t\treturn map[string]interface{}{\n\t\t\t\"jsonrpc\": \"2.0\",\n\t\t\t\"error\":   rerr,\n\t\t\t\"id\":      data[\"id\"],\n\t\t}\n\t}))\n}\n"
  },
  {
    "path": "test/busi/base_types.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage busi\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/gin-gonic/gin\"\n\t\"go.mongodb.org/mongo-driver/bson\"\n)\n\n// StoreHost examples will connect to dtm.pub; tests will connect to localhost\nvar StoreHost = \"localhost\"\n\n// BusiConf 1\nvar BusiConf = dtmcli.DBConf{\n\tDriver: \"mysql\",\n\tHost:   StoreHost,\n\tPort:   3306,\n\tUser:   \"root\",\n}\n\n// UserAccount 1\ntype UserAccount struct {\n\tUserID         int\n\tBalance        string\n\tTradingBalance string\n}\n\n// TableName 1\nfunc (*UserAccount) TableName() string {\n\treturn \"dtm_busi.user_account\"\n}\n\n// GetBalanceByUID 1\nfunc GetBalanceByUID(uid int, store string) int {\n\tif store == \"redis\" {\n\t\trd := RedisGet()\n\t\taccA, err := rd.Get(context.Background(), GetRedisAccountKey(uid)).Result()\n\t\tdtmimp.E2P(err)\n\t\treturn dtmimp.MustAtoi(accA)\n\t} else if store == \"mongo\" {\n\t\tmg := MongoGet()\n\t\taccount := mg.Database(\"dtm_busi\").Collection(\"user_account\")\n\t\tvar result bson.M\n\t\terr := account.FindOne(context.Background(), bson.D{{Key: \"user_id\", Value: uid}}).Decode(&result)\n\t\tdtmimp.E2P(err)\n\t\treturn int(result[\"balance\"].(float64))\n\t}\n\tua := UserAccount{}\n\t_ = dbGet().Must().Model(&ua).Where(\"user_id=?\", uid).First(&ua)\n\treturn dtmimp.MustAtoi(ua.Balance[:len(ua.Balance)-3])\n}\n\n// ReqHTTP transaction request payload\ntype ReqHTTP struct {\n\tAmount         int    `json:\"amount\"`\n\tTransInResult  string `json:\"trans_in_result\"`\n\tTransOutResult string `json:\"trans_out_Result\"`\n\tStore          string `json:\"store\"` // default mysql, value can be mysql|redis\n}\n\nfunc (t *ReqHTTP) String() string {\n\treturn fmt.Sprintf(\"amount: %d transIn: %s transOut: %s\", t.Amount, t.TransInResult, t.TransOutResult)\n}\n\n// GenReqHTTP 1\nfunc GenReqHTTP(amount int, outFailed bool, inFailed bool) *ReqHTTP {\n\treturn &ReqHTTP{\n\t\tAmount:         amount,\n\t\tTransOutResult: dtmimp.If(outFailed, dtmcli.ResultFailure, \"\").(string),\n\t\tTransInResult:  dtmimp.If(inFailed, dtmcli.ResultFailure, \"\").(string),\n\t}\n}\n\n// GenReqGrpc 1\nfunc GenReqGrpc(amount int, outFailed bool, inFailed bool) *ReqGrpc {\n\treturn &ReqGrpc{\n\t\tAmount:         int64(amount),\n\t\tTransOutResult: dtmimp.If(outFailed, dtmcli.ResultFailure, \"\").(string),\n\t\tTransInResult:  dtmimp.If(inFailed, dtmcli.ResultFailure, \"\").(string),\n\t}\n}\n\nfunc reqFrom(c *gin.Context) *ReqHTTP {\n\tv, ok := c.Get(\"trans_req\")\n\tif !ok {\n\t\treq := ReqHTTP{}\n\t\terr := c.BindJSON(&req)\n\t\tlogger.FatalIfError(err)\n\t\tc.Set(\"trans_req\", &req)\n\t\tv = &req\n\t}\n\treturn v.(*ReqHTTP)\n}\n\nfunc infoFromContext(c *gin.Context) *dtmcli.BranchBarrier {\n\tinfo := dtmcli.BranchBarrier{\n\t\tTransType: c.Query(\"trans_type\"),\n\t\tGid:       c.Query(\"gid\"),\n\t\tBranchID:  c.Query(\"branch_id\"),\n\t\tOp:        c.Query(\"op\"),\n\t}\n\treturn &info\n}\n\n// AutoEmptyString auto reset to empty when used once\ntype AutoEmptyString struct {\n\tvalue string\n}\n\n// SetOnce set a value once\nfunc (s *AutoEmptyString) SetOnce(v string) {\n\ts.value = v\n}\n\n// Fetch fetch the stored value, then reset the value to empty\nfunc (s *AutoEmptyString) Fetch() string {\n\tv := s.value\n\ts.value = \"\"\n\tif v != \"\" {\n\t\tlogger.Debugf(\"fetch obtain not empty value: %s\", v)\n\t}\n\treturn v\n}\n\ntype mainSwitchType struct {\n\tTransInResult         AutoEmptyString\n\tTransOutResult        AutoEmptyString\n\tTransInConfirmResult  AutoEmptyString\n\tTransOutConfirmResult AutoEmptyString\n\tTransInRevertResult   AutoEmptyString\n\tTransOutRevertResult  AutoEmptyString\n\tQueryPreparedResult   AutoEmptyString\n\tNextResult            AutoEmptyString\n\tJrpcResult            AutoEmptyString\n\tFailureReason         AutoEmptyString\n}\n\n// MainSwitch controls busi success or fail\nvar MainSwitch mainSwitchType\n\n// GetRedisAccountKey return redis key for uid\nfunc GetRedisAccountKey(uid int) string {\n\treturn fmt.Sprintf(\"{a}-redis-account-key-%d\", uid)\n}\n"
  },
  {
    "path": "test/busi/busi.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.28.0\n// \tprotoc        v3.17.3\n// source: test/busi/busi.proto\n\npackage busi\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\temptypb \"google.golang.org/protobuf/types/known/emptypb\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\n// DtmRequest request sent to dtm server\ntype ReqGrpc struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tAmount         int64  `protobuf:\"varint,1,opt,name=Amount,proto3\" json:\"Amount,omitempty\"`\n\tTransOutResult string `protobuf:\"bytes,2,opt,name=TransOutResult,proto3\" json:\"TransOutResult,omitempty\"`\n\tTransInResult  string `protobuf:\"bytes,3,opt,name=TransInResult,proto3\" json:\"TransInResult,omitempty\"`\n}\n\nfunc (x *ReqGrpc) Reset() {\n\t*x = ReqGrpc{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_test_busi_busi_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *ReqGrpc) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*ReqGrpc) ProtoMessage() {}\n\nfunc (x *ReqGrpc) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_busi_busi_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use ReqGrpc.ProtoReflect.Descriptor instead.\nfunc (*ReqGrpc) Descriptor() ([]byte, []int) {\n\treturn file_test_busi_busi_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *ReqGrpc) GetAmount() int64 {\n\tif x != nil {\n\t\treturn x.Amount\n\t}\n\treturn 0\n}\n\nfunc (x *ReqGrpc) GetTransOutResult() string {\n\tif x != nil {\n\t\treturn x.TransOutResult\n\t}\n\treturn \"\"\n}\n\nfunc (x *ReqGrpc) GetTransInResult() string {\n\tif x != nil {\n\t\treturn x.TransInResult\n\t}\n\treturn \"\"\n}\n\ntype BusiReply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tMessage string `protobuf:\"bytes,1,opt,name=message,proto3\" json:\"message,omitempty\"`\n}\n\nfunc (x *BusiReply) Reset() {\n\t*x = BusiReply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_test_busi_busi_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *BusiReply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*BusiReply) ProtoMessage() {}\n\nfunc (x *BusiReply) ProtoReflect() protoreflect.Message {\n\tmi := &file_test_busi_busi_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use BusiReply.ProtoReflect.Descriptor instead.\nfunc (*BusiReply) Descriptor() ([]byte, []int) {\n\treturn file_test_busi_busi_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *BusiReply) GetMessage() string {\n\tif x != nil {\n\t\treturn x.Message\n\t}\n\treturn \"\"\n}\n\nvar File_test_busi_busi_proto protoreflect.FileDescriptor\n\nvar file_test_busi_busi_proto_rawDesc = []byte{\n\t0x0a, 0x14, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x62, 0x75, 0x73, 0x69, 0x2f, 0x62, 0x75, 0x73, 0x69,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x62, 0x75, 0x73, 0x69, 0x1a, 0x1b, 0x67, 0x6f,\n\t0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d,\n\t0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6f, 0x0a, 0x07, 0x52, 0x65, 0x71,\n\t0x47, 0x72, 0x70, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01,\n\t0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0e,\n\t0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02,\n\t0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x52, 0x65,\n\t0x73, 0x75, 0x6c, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x49, 0x6e, 0x52,\n\t0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x54, 0x72, 0x61,\n\t0x6e, 0x73, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x25, 0x0a, 0x09, 0x42, 0x75,\n\t0x73, 0x69, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,\n\t0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,\n\t0x65, 0x32, 0xbe, 0x0b, 0x0a, 0x04, 0x42, 0x75, 0x73, 0x69, 0x12, 0x32, 0x0a, 0x07, 0x54, 0x72,\n\t0x61, 0x6e, 0x73, 0x49, 0x6e, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x52, 0x65, 0x71,\n\t0x47, 0x72, 0x70, 0x63, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x33,\n\t0x0a, 0x08, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73,\n\t0x69, 0x2e, 0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,\n\t0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,\n\t0x79, 0x22, 0x00, 0x12, 0x38, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x49, 0x6e, 0x52, 0x65,\n\t0x76, 0x65, 0x72, 0x74, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x47,\n\t0x72, 0x70, 0x63, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x39, 0x0a,\n\t0x0e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x12,\n\t0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x16,\n\t0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,\n\t0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x0e, 0x54, 0x72, 0x61, 0x6e,\n\t0x73, 0x49, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73,\n\t0x69, 0x2e, 0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,\n\t0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,\n\t0x79, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x43,\n\t0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x52, 0x65,\n\t0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12,\n\t0x3c, 0x0a, 0x08, 0x58, 0x61, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x16, 0x2e, 0x67, 0x6f,\n\t0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,\n\t0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x34, 0x0a,\n\t0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x49, 0x6e, 0x58, 0x61, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73,\n\t0x69, 0x2e, 0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,\n\t0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,\n\t0x79, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x58,\n\t0x61, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63,\n\t0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,\n\t0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x0a, 0x54, 0x72,\n\t0x61, 0x6e, 0x73, 0x49, 0x6e, 0x54, 0x63, 0x63, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e,\n\t0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,\n\t0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22,\n\t0x00, 0x12, 0x36, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x54, 0x63, 0x63,\n\t0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63, 0x1a,\n\t0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,\n\t0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x10, 0x54, 0x72, 0x61,\n\t0x6e, 0x73, 0x49, 0x6e, 0x54, 0x63, 0x63, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x0d, 0x2e,\n\t0x62, 0x75, 0x73, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x16, 0x2e, 0x67,\n\t0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,\n\t0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x49,\n\t0x6e, 0x42, 0x53, 0x61, 0x67, 0x61, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x52, 0x65,\n\t0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12,\n\t0x38, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x42, 0x53, 0x61, 0x67, 0x61,\n\t0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63, 0x1a,\n\t0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,\n\t0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x12, 0x54, 0x72, 0x61,\n\t0x6e, 0x73, 0x49, 0x6e, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x42, 0x53, 0x61, 0x67, 0x61, 0x12,\n\t0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x16,\n\t0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,\n\t0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x13, 0x54, 0x72, 0x61, 0x6e,\n\t0x73, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x42, 0x53, 0x61, 0x67, 0x61, 0x12,\n\t0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x16,\n\t0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,\n\t0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3c, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x6e,\n\t0x73, 0x4f, 0x75, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x59, 0x65, 0x73, 0x12, 0x0d, 0x2e,\n\t0x62, 0x75, 0x73, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x16, 0x2e, 0x67,\n\t0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,\n\t0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x10, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f,\n\t0x75, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4e, 0x6f, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73,\n\t0x69, 0x2e, 0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,\n\t0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,\n\t0x79, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x49, 0x6e, 0x52, 0x65,\n\t0x64, 0x69, 0x73, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x47, 0x72,\n\t0x70, 0x63, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x38, 0x0a, 0x0d,\n\t0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x64, 0x69, 0x73, 0x12, 0x0d, 0x2e,\n\t0x62, 0x75, 0x73, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x16, 0x2e, 0x67,\n\t0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,\n\t0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x12, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x49,\n\t0x6e, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x52, 0x65, 0x64, 0x69, 0x73, 0x12, 0x0d, 0x2e, 0x62,\n\t0x75, 0x73, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x16, 0x2e, 0x67, 0x6f,\n\t0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,\n\t0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x13, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x4f, 0x75,\n\t0x74, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x52, 0x65, 0x64, 0x69, 0x73, 0x12, 0x0d, 0x2e, 0x62,\n\t0x75, 0x73, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x16, 0x2e, 0x67, 0x6f,\n\t0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,\n\t0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72,\n\t0x65, 0x70, 0x61, 0x72, 0x65, 0x64, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x52, 0x65,\n\t0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x0f, 0x2e, 0x62, 0x75, 0x73, 0x69, 0x2e, 0x42, 0x75, 0x73,\n\t0x69, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x0e, 0x51, 0x75, 0x65, 0x72,\n\t0x79, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x64, 0x42, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73,\n\t0x69, 0x2e, 0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,\n\t0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,\n\t0x79, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x12, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x65, 0x70,\n\t0x61, 0x72, 0x65, 0x64, 0x52, 0x65, 0x64, 0x69, 0x73, 0x12, 0x0d, 0x2e, 0x62, 0x75, 0x73, 0x69,\n\t0x2e, 0x52, 0x65, 0x71, 0x47, 0x72, 0x70, 0x63, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,\n\t0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,\n\t0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2e, 0x2f, 0x62, 0x75, 0x73, 0x69, 0x62, 0x06, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_test_busi_busi_proto_rawDescOnce sync.Once\n\tfile_test_busi_busi_proto_rawDescData = file_test_busi_busi_proto_rawDesc\n)\n\nfunc file_test_busi_busi_proto_rawDescGZIP() []byte {\n\tfile_test_busi_busi_proto_rawDescOnce.Do(func() {\n\t\tfile_test_busi_busi_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_busi_busi_proto_rawDescData)\n\t})\n\treturn file_test_busi_busi_proto_rawDescData\n}\n\nvar file_test_busi_busi_proto_msgTypes = make([]protoimpl.MessageInfo, 2)\nvar file_test_busi_busi_proto_goTypes = []interface{}{\n\t(*ReqGrpc)(nil),       // 0: busi.ReqGrpc\n\t(*BusiReply)(nil),     // 1: busi.BusiReply\n\t(*emptypb.Empty)(nil), // 2: google.protobuf.Empty\n}\nvar file_test_busi_busi_proto_depIdxs = []int32{\n\t0,  // 0: busi.Busi.TransIn:input_type -> busi.ReqGrpc\n\t0,  // 1: busi.Busi.TransOut:input_type -> busi.ReqGrpc\n\t0,  // 2: busi.Busi.TransInRevert:input_type -> busi.ReqGrpc\n\t0,  // 3: busi.Busi.TransOutRevert:input_type -> busi.ReqGrpc\n\t0,  // 4: busi.Busi.TransInConfirm:input_type -> busi.ReqGrpc\n\t0,  // 5: busi.Busi.TransOutConfirm:input_type -> busi.ReqGrpc\n\t2,  // 6: busi.Busi.XaNotify:input_type -> google.protobuf.Empty\n\t0,  // 7: busi.Busi.TransInXa:input_type -> busi.ReqGrpc\n\t0,  // 8: busi.Busi.TransOutXa:input_type -> busi.ReqGrpc\n\t0,  // 9: busi.Busi.TransInTcc:input_type -> busi.ReqGrpc\n\t0,  // 10: busi.Busi.TransOutTcc:input_type -> busi.ReqGrpc\n\t0,  // 11: busi.Busi.TransInTccNested:input_type -> busi.ReqGrpc\n\t0,  // 12: busi.Busi.TransInBSaga:input_type -> busi.ReqGrpc\n\t0,  // 13: busi.Busi.TransOutBSaga:input_type -> busi.ReqGrpc\n\t0,  // 14: busi.Busi.TransInRevertBSaga:input_type -> busi.ReqGrpc\n\t0,  // 15: busi.Busi.TransOutRevertBSaga:input_type -> busi.ReqGrpc\n\t0,  // 16: busi.Busi.TransOutHeaderYes:input_type -> busi.ReqGrpc\n\t0,  // 17: busi.Busi.TransOutHeaderNo:input_type -> busi.ReqGrpc\n\t0,  // 18: busi.Busi.TransInRedis:input_type -> busi.ReqGrpc\n\t0,  // 19: busi.Busi.TransOutRedis:input_type -> busi.ReqGrpc\n\t0,  // 20: busi.Busi.TransInRevertRedis:input_type -> busi.ReqGrpc\n\t0,  // 21: busi.Busi.TransOutRevertRedis:input_type -> busi.ReqGrpc\n\t0,  // 22: busi.Busi.QueryPrepared:input_type -> busi.ReqGrpc\n\t0,  // 23: busi.Busi.QueryPreparedB:input_type -> busi.ReqGrpc\n\t0,  // 24: busi.Busi.QueryPreparedRedis:input_type -> busi.ReqGrpc\n\t2,  // 25: busi.Busi.TransIn:output_type -> google.protobuf.Empty\n\t2,  // 26: busi.Busi.TransOut:output_type -> google.protobuf.Empty\n\t2,  // 27: busi.Busi.TransInRevert:output_type -> google.protobuf.Empty\n\t2,  // 28: busi.Busi.TransOutRevert:output_type -> google.protobuf.Empty\n\t2,  // 29: busi.Busi.TransInConfirm:output_type -> google.protobuf.Empty\n\t2,  // 30: busi.Busi.TransOutConfirm:output_type -> google.protobuf.Empty\n\t2,  // 31: busi.Busi.XaNotify:output_type -> google.protobuf.Empty\n\t2,  // 32: busi.Busi.TransInXa:output_type -> google.protobuf.Empty\n\t2,  // 33: busi.Busi.TransOutXa:output_type -> google.protobuf.Empty\n\t2,  // 34: busi.Busi.TransInTcc:output_type -> google.protobuf.Empty\n\t2,  // 35: busi.Busi.TransOutTcc:output_type -> google.protobuf.Empty\n\t2,  // 36: busi.Busi.TransInTccNested:output_type -> google.protobuf.Empty\n\t2,  // 37: busi.Busi.TransInBSaga:output_type -> google.protobuf.Empty\n\t2,  // 38: busi.Busi.TransOutBSaga:output_type -> google.protobuf.Empty\n\t2,  // 39: busi.Busi.TransInRevertBSaga:output_type -> google.protobuf.Empty\n\t2,  // 40: busi.Busi.TransOutRevertBSaga:output_type -> google.protobuf.Empty\n\t2,  // 41: busi.Busi.TransOutHeaderYes:output_type -> google.protobuf.Empty\n\t2,  // 42: busi.Busi.TransOutHeaderNo:output_type -> google.protobuf.Empty\n\t2,  // 43: busi.Busi.TransInRedis:output_type -> google.protobuf.Empty\n\t2,  // 44: busi.Busi.TransOutRedis:output_type -> google.protobuf.Empty\n\t2,  // 45: busi.Busi.TransInRevertRedis:output_type -> google.protobuf.Empty\n\t2,  // 46: busi.Busi.TransOutRevertRedis:output_type -> google.protobuf.Empty\n\t1,  // 47: busi.Busi.QueryPrepared:output_type -> busi.BusiReply\n\t2,  // 48: busi.Busi.QueryPreparedB:output_type -> google.protobuf.Empty\n\t2,  // 49: busi.Busi.QueryPreparedRedis:output_type -> google.protobuf.Empty\n\t25, // [25:50] is the sub-list for method output_type\n\t0,  // [0:25] is the sub-list for method input_type\n\t0,  // [0:0] is the sub-list for extension type_name\n\t0,  // [0:0] is the sub-list for extension extendee\n\t0,  // [0:0] is the sub-list for field type_name\n}\n\nfunc init() { file_test_busi_busi_proto_init() }\nfunc file_test_busi_busi_proto_init() {\n\tif File_test_busi_busi_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_test_busi_busi_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*ReqGrpc); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_test_busi_busi_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*BusiReply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_test_busi_busi_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   2,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_test_busi_busi_proto_goTypes,\n\t\tDependencyIndexes: file_test_busi_busi_proto_depIdxs,\n\t\tMessageInfos:      file_test_busi_busi_proto_msgTypes,\n\t}.Build()\n\tFile_test_busi_busi_proto = out.File\n\tfile_test_busi_busi_proto_rawDesc = nil\n\tfile_test_busi_busi_proto_goTypes = nil\n\tfile_test_busi_busi_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "test/busi/busi.proto",
    "content": "syntax = \"proto3\";\n\npackage busi;\nimport \"google/protobuf/empty.proto\";\n\noption go_package = \"./busi\";\n\n// DtmRequest request sent to dtm server\nmessage ReqGrpc {\n  int64 Amount = 1;\n  string TransOutResult = 2;\n  string TransInResult = 3;\n}\n\nmessage BusiReply {\n  string message = 1;\n}\n// The dtm service definition.\nservice Busi {\n  rpc TransIn(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc TransOut(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc TransInRevert(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc TransOutRevert(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc TransInConfirm(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc TransOutConfirm(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc XaNotify(google.protobuf.Empty) returns (google.protobuf.Empty) {}\n\n  rpc TransInXa(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc TransOutXa(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc TransInTcc(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc TransOutTcc(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc TransInTccNested(ReqGrpc) returns (google.protobuf.Empty) {}\n\n  rpc TransInBSaga(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc TransOutBSaga(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc TransInRevertBSaga(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc TransOutRevertBSaga(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc TransOutHeaderYes(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc TransOutHeaderNo(ReqGrpc) returns (google.protobuf.Empty) {}\n\n  rpc TransInRedis(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc TransOutRedis(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc TransInRevertRedis(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc TransOutRevertRedis(ReqGrpc) returns (google.protobuf.Empty) {}\n\n  rpc QueryPrepared(ReqGrpc) returns (BusiReply) {}\n  rpc QueryPreparedB(ReqGrpc) returns (google.protobuf.Empty) {}\n  rpc QueryPreparedRedis(ReqGrpc) returns (google.protobuf.Empty) {}\n}\n\n"
  },
  {
    "path": "test/busi/busi_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.2.0\n// - protoc             v3.17.3\n// source: test/busi/busi.proto\n\npackage busi\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n\temptypb \"google.golang.org/protobuf/types/known/emptypb\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\n// Requires gRPC-Go v1.32.0 or later.\nconst _ = grpc.SupportPackageIsVersion7\n\n// BusiClient is the client API for Busi service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\ntype BusiClient interface {\n\tTransIn(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransOut(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransInRevert(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransOutRevert(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransInConfirm(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransOutConfirm(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tXaNotify(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransInXa(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransOutXa(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransInTcc(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransOutTcc(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransInTccNested(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransInBSaga(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransOutBSaga(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransInRevertBSaga(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransOutRevertBSaga(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransOutHeaderYes(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransOutHeaderNo(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransInRedis(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransOutRedis(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransInRevertRedis(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tTransOutRevertRedis(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tQueryPrepared(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*BusiReply, error)\n\tQueryPreparedB(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n\tQueryPreparedRedis(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error)\n}\n\ntype busiClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewBusiClient(cc grpc.ClientConnInterface) BusiClient {\n\treturn &busiClient{cc}\n}\n\nfunc (c *busiClient) TransIn(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransIn\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransOut(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransOut\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransInRevert(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransInRevert\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransOutRevert(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransOutRevert\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransInConfirm(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransInConfirm\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransOutConfirm(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransOutConfirm\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) XaNotify(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/XaNotify\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransInXa(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransInXa\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransOutXa(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransOutXa\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransInTcc(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransInTcc\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransOutTcc(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransOutTcc\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransInTccNested(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransInTccNested\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransInBSaga(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransInBSaga\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransOutBSaga(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransOutBSaga\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransInRevertBSaga(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransInRevertBSaga\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransOutRevertBSaga(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransOutRevertBSaga\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransOutHeaderYes(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransOutHeaderYes\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransOutHeaderNo(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransOutHeaderNo\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransInRedis(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransInRedis\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransOutRedis(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransOutRedis\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransInRevertRedis(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransInRevertRedis\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) TransOutRevertRedis(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/TransOutRevertRedis\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) QueryPrepared(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*BusiReply, error) {\n\tout := new(BusiReply)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/QueryPrepared\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) QueryPreparedB(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/QueryPreparedB\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\nfunc (c *busiClient) QueryPreparedRedis(ctx context.Context, in *ReqGrpc, opts ...grpc.CallOption) (*emptypb.Empty, error) {\n\tout := new(emptypb.Empty)\n\terr := c.cc.Invoke(ctx, \"/busi.Busi/QueryPreparedRedis\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// BusiServer is the server API for Busi service.\n// All implementations must embed UnimplementedBusiServer\n// for forward compatibility\ntype BusiServer interface {\n\tTransIn(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransOut(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransInRevert(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransOutRevert(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransInConfirm(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransOutConfirm(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tXaNotify(context.Context, *emptypb.Empty) (*emptypb.Empty, error)\n\tTransInXa(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransOutXa(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransInTcc(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransOutTcc(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransInTccNested(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransInBSaga(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransOutBSaga(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransInRevertBSaga(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransOutRevertBSaga(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransOutHeaderYes(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransOutHeaderNo(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransInRedis(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransOutRedis(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransInRevertRedis(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tTransOutRevertRedis(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tQueryPrepared(context.Context, *ReqGrpc) (*BusiReply, error)\n\tQueryPreparedB(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tQueryPreparedRedis(context.Context, *ReqGrpc) (*emptypb.Empty, error)\n\tmustEmbedUnimplementedBusiServer()\n}\n\n// UnimplementedBusiServer must be embedded to have forward compatible implementations.\ntype UnimplementedBusiServer struct {\n}\n\nfunc (UnimplementedBusiServer) TransIn(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransIn not implemented\")\n}\nfunc (UnimplementedBusiServer) TransOut(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransOut not implemented\")\n}\nfunc (UnimplementedBusiServer) TransInRevert(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransInRevert not implemented\")\n}\nfunc (UnimplementedBusiServer) TransOutRevert(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransOutRevert not implemented\")\n}\nfunc (UnimplementedBusiServer) TransInConfirm(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransInConfirm not implemented\")\n}\nfunc (UnimplementedBusiServer) TransOutConfirm(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransOutConfirm not implemented\")\n}\nfunc (UnimplementedBusiServer) XaNotify(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method XaNotify not implemented\")\n}\nfunc (UnimplementedBusiServer) TransInXa(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransInXa not implemented\")\n}\nfunc (UnimplementedBusiServer) TransOutXa(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransOutXa not implemented\")\n}\nfunc (UnimplementedBusiServer) TransInTcc(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransInTcc not implemented\")\n}\nfunc (UnimplementedBusiServer) TransOutTcc(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransOutTcc not implemented\")\n}\nfunc (UnimplementedBusiServer) TransInTccNested(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransInTccNested not implemented\")\n}\nfunc (UnimplementedBusiServer) TransInBSaga(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransInBSaga not implemented\")\n}\nfunc (UnimplementedBusiServer) TransOutBSaga(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransOutBSaga not implemented\")\n}\nfunc (UnimplementedBusiServer) TransInRevertBSaga(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransInRevertBSaga not implemented\")\n}\nfunc (UnimplementedBusiServer) TransOutRevertBSaga(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransOutRevertBSaga not implemented\")\n}\nfunc (UnimplementedBusiServer) TransOutHeaderYes(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransOutHeaderYes not implemented\")\n}\nfunc (UnimplementedBusiServer) TransOutHeaderNo(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransOutHeaderNo not implemented\")\n}\nfunc (UnimplementedBusiServer) TransInRedis(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransInRedis not implemented\")\n}\nfunc (UnimplementedBusiServer) TransOutRedis(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransOutRedis not implemented\")\n}\nfunc (UnimplementedBusiServer) TransInRevertRedis(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransInRevertRedis not implemented\")\n}\nfunc (UnimplementedBusiServer) TransOutRevertRedis(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method TransOutRevertRedis not implemented\")\n}\nfunc (UnimplementedBusiServer) QueryPrepared(context.Context, *ReqGrpc) (*BusiReply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method QueryPrepared not implemented\")\n}\nfunc (UnimplementedBusiServer) QueryPreparedB(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method QueryPreparedB not implemented\")\n}\nfunc (UnimplementedBusiServer) QueryPreparedRedis(context.Context, *ReqGrpc) (*emptypb.Empty, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method QueryPreparedRedis not implemented\")\n}\nfunc (UnimplementedBusiServer) mustEmbedUnimplementedBusiServer() {}\n\n// UnsafeBusiServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to BusiServer will\n// result in compilation errors.\ntype UnsafeBusiServer interface {\n\tmustEmbedUnimplementedBusiServer()\n}\n\nfunc RegisterBusiServer(s grpc.ServiceRegistrar, srv BusiServer) {\n\ts.RegisterService(&Busi_ServiceDesc, srv)\n}\n\nfunc _Busi_TransIn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransIn(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransIn\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransIn(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransOut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransOut(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransOut\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransOut(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransInRevert_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransInRevert(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransInRevert\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransInRevert(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransOutRevert_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransOutRevert(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransOutRevert\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransOutRevert(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransInConfirm_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransInConfirm(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransInConfirm\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransInConfirm(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransOutConfirm_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransOutConfirm(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransOutConfirm\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransOutConfirm(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_XaNotify_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(emptypb.Empty)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).XaNotify(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/XaNotify\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).XaNotify(ctx, req.(*emptypb.Empty))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransInXa_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransInXa(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransInXa\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransInXa(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransOutXa_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransOutXa(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransOutXa\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransOutXa(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransInTcc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransInTcc(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransInTcc\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransInTcc(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransOutTcc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransOutTcc(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransOutTcc\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransOutTcc(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransInTccNested_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransInTccNested(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransInTccNested\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransInTccNested(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransInBSaga_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransInBSaga(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransInBSaga\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransInBSaga(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransOutBSaga_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransOutBSaga(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransOutBSaga\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransOutBSaga(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransInRevertBSaga_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransInRevertBSaga(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransInRevertBSaga\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransInRevertBSaga(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransOutRevertBSaga_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransOutRevertBSaga(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransOutRevertBSaga\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransOutRevertBSaga(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransOutHeaderYes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransOutHeaderYes(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransOutHeaderYes\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransOutHeaderYes(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransOutHeaderNo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransOutHeaderNo(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransOutHeaderNo\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransOutHeaderNo(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransInRedis_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransInRedis(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransInRedis\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransInRedis(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransOutRedis_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransOutRedis(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransOutRedis\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransOutRedis(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransInRevertRedis_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransInRevertRedis(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransInRevertRedis\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransInRevertRedis(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_TransOutRevertRedis_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).TransOutRevertRedis(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/TransOutRevertRedis\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).TransOutRevertRedis(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_QueryPrepared_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).QueryPrepared(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/QueryPrepared\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).QueryPrepared(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_QueryPreparedB_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).QueryPreparedB(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/QueryPreparedB\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).QueryPreparedB(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nfunc _Busi_QueryPreparedRedis_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(ReqGrpc)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(BusiServer).QueryPreparedRedis(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/busi.Busi/QueryPreparedRedis\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(BusiServer).QueryPreparedRedis(ctx, req.(*ReqGrpc))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// Busi_ServiceDesc is the grpc.ServiceDesc for Busi service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar Busi_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"busi.Busi\",\n\tHandlerType: (*BusiServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"TransIn\",\n\t\t\tHandler:    _Busi_TransIn_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransOut\",\n\t\t\tHandler:    _Busi_TransOut_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransInRevert\",\n\t\t\tHandler:    _Busi_TransInRevert_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransOutRevert\",\n\t\t\tHandler:    _Busi_TransOutRevert_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransInConfirm\",\n\t\t\tHandler:    _Busi_TransInConfirm_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransOutConfirm\",\n\t\t\tHandler:    _Busi_TransOutConfirm_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"XaNotify\",\n\t\t\tHandler:    _Busi_XaNotify_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransInXa\",\n\t\t\tHandler:    _Busi_TransInXa_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransOutXa\",\n\t\t\tHandler:    _Busi_TransOutXa_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransInTcc\",\n\t\t\tHandler:    _Busi_TransInTcc_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransOutTcc\",\n\t\t\tHandler:    _Busi_TransOutTcc_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransInTccNested\",\n\t\t\tHandler:    _Busi_TransInTccNested_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransInBSaga\",\n\t\t\tHandler:    _Busi_TransInBSaga_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransOutBSaga\",\n\t\t\tHandler:    _Busi_TransOutBSaga_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransInRevertBSaga\",\n\t\t\tHandler:    _Busi_TransInRevertBSaga_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransOutRevertBSaga\",\n\t\t\tHandler:    _Busi_TransOutRevertBSaga_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransOutHeaderYes\",\n\t\t\tHandler:    _Busi_TransOutHeaderYes_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransOutHeaderNo\",\n\t\t\tHandler:    _Busi_TransOutHeaderNo_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransInRedis\",\n\t\t\tHandler:    _Busi_TransInRedis_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransOutRedis\",\n\t\t\tHandler:    _Busi_TransOutRedis_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransInRevertRedis\",\n\t\t\tHandler:    _Busi_TransInRevertRedis_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"TransOutRevertRedis\",\n\t\t\tHandler:    _Busi_TransOutRevertRedis_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"QueryPrepared\",\n\t\t\tHandler:    _Busi_QueryPrepared_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"QueryPreparedB\",\n\t\t\tHandler:    _Busi_QueryPreparedB_Handler,\n\t\t},\n\t\t{\n\t\t\tMethodName: \"QueryPreparedRedis\",\n\t\t\tHandler:    _Busi_QueryPreparedRedis_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"test/busi/busi.proto\",\n}\n"
  },
  {
    "path": "test/busi/data.go",
    "content": "package busi\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/gin-gonic/gin\"\n\t\"go.mongodb.org/mongo-driver/bson\"\n\t\"go.mongodb.org/mongo-driver/mongo\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n)\n\n// PopulateDB populate example mysql data\nfunc PopulateDB(skipDrop bool) {\n\tResetXaData()\n\tfile := fmt.Sprintf(\"%s/busi.%s.sql\", dtmutil.GetSQLDir(), BusiConf.Driver)\n\tdtmutil.RunSQLScript(BusiConf, file, skipDrop)\n\tfile = fmt.Sprintf(\"%s/dtmcli.barrier.%s.sql\", dtmutil.GetSQLDir(), BusiConf.Driver)\n\tdtmutil.RunSQLScript(BusiConf, file, skipDrop)\n\tfile = fmt.Sprintf(\"%s/dtmsvr.storage.%s.sql\", dtmutil.GetSQLDir(), BusiConf.Driver)\n\tdtmutil.RunSQLScript(BusiConf, file, skipDrop)\n\t_, err := RedisGet().FlushAll(context.Background()).Result() // redis barrier need clear\n\tdtmimp.E2P(err)\n\tSetRedisBothAccount(10000, 10000)\n\tSetupMongoBarrierAndBusi()\n}\n\n// TransOutUID 1\nconst TransOutUID = 1\n\n// TransInUID 2\nconst TransInUID = 2\n\n// Redis 1\nconst Redis = \"redis\"\n\n// Mongo 1\nconst Mongo = \"mongo\"\n\nfunc handleGrpcBusiness(in *ReqGrpc, result1 string, result2 string, busi string) error {\n\tres := dtmimp.OrString(result1, result2, dtmcli.ResultSuccess)\n\tlogger.Debugf(\"grpc busi %s %v %s %s result: %s\", busi, in, result1, result2, res)\n\tif res == dtmcli.ResultSuccess {\n\t\treturn nil\n\t} else if res == dtmcli.ResultFailure {\n\t\treturn status.New(codes.Aborted, fmt.Sprintf(\"reason:%s\", MainSwitch.FailureReason.Fetch())).Err()\n\t} else if res == dtmcli.ResultOngoing {\n\t\treturn status.New(codes.FailedPrecondition, dtmcli.ResultOngoing).Err()\n\t}\n\treturn status.New(codes.Internal, fmt.Sprintf(\"unknow result %s\", res)).Err()\n}\n\nfunc handleGeneralBusiness(c *gin.Context, result1 string, result2 string, busi string) interface{} {\n\tinfo := infoFromContext(c)\n\tres := dtmimp.OrString(result1, result2, dtmcli.ResultSuccess)\n\tlogger.Debugf(\"%s %s result: %s\", busi, info.String(), res)\n\tif res == \"ERROR\" {\n\t\treturn errors.New(\"ERROR from user\")\n\t}\n\tif res == dtmimp.ResultFailure {\n\t\treturn fmt.Errorf(\"reason:%s. %w\", MainSwitch.FailureReason.Fetch(), dtmimp.ErrFailure)\n\t}\n\treturn string2DtmError(res)\n}\n\n// old business handler. for compatible usage\nfunc handleGeneralBusinessCompatible(c *gin.Context, result1 string, result2 string, busi string) (interface{}, error) {\n\tinfo := infoFromContext(c)\n\tres := dtmimp.OrString(result1, result2, dtmcli.ResultSuccess)\n\tlogger.Debugf(\"%s %s result: %s\", busi, info.String(), res)\n\tif res == \"ERROR\" {\n\t\treturn nil, errors.New(\"ERROR from user\")\n\t}\n\treturn map[string]interface{}{\"dtm_result\": res}, nil\n}\n\nfunc sagaGrpcAdjustBalance(db dtmcli.DB, uid int, amount int64, result string) error {\n\tif result == dtmcli.ResultFailure {\n\t\treturn status.New(codes.Aborted, dtmcli.ResultFailure).Err()\n\t}\n\t_, err := dtmimp.DBExec(BusiConf.Driver, db, \"update dtm_busi.user_account set balance = balance + ? where user_id = ?\", amount, uid)\n\treturn err\n}\n\n// SagaAdjustBalance 1\nfunc SagaAdjustBalance(db dtmcli.DB, uid int, amount int, result string) error {\n\tif strings.Contains(result, dtmcli.ResultFailure) {\n\t\treturn dtmcli.ErrFailure\n\t}\n\t_, err := dtmimp.DBExec(BusiConf.Driver, db, \"update dtm_busi.user_account set balance = balance + ? where user_id = ?\", amount, uid)\n\treturn err\n}\n\n// SagaMongoAdjustBalance 1\nfunc SagaMongoAdjustBalance(ctx context.Context, mc *mongo.Client, uid int, amount int, result string) error {\n\tif strings.Contains(result, dtmcli.ResultFailure) {\n\t\treturn dtmcli.ErrFailure\n\t}\n\t_, err := mc.Database(\"dtm_busi\").Collection(\"user_account\").UpdateOne(ctx,\n\t\tbson.D{{Key: \"user_id\", Value: uid}},\n\t\tbson.D{{Key: \"$inc\", Value: bson.D{{Key: \"balance\", Value: amount}}}})\n\tlogger.Debugf(\"dtm_busi.user_account $inc balance of %d by %d err: %v\", uid, amount, err)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar res bson.M\n\terr = mc.Database(\"dtm_busi\").Collection(\"user_account\").FindOne(ctx,\n\t\tbson.D{{Key: \"user_id\", Value: uid}}).Decode(&res)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbalance := res[\"balance\"].(float64)\n\tif balance < 0 {\n\t\treturn fmt.Errorf(\"balance not enough %w\", dtmcli.ErrFailure)\n\t}\n\treturn nil\n}\n\nfunc tccAdjustTrading(db dtmcli.DB, uid int, amount int) error {\n\taffected, err := dtmimp.DBExec(BusiConf.Driver, db, `update dtm_busi.user_account\n\t\tset trading_balance=trading_balance+?\n\t\twhere user_id=? and trading_balance + ? + balance >= 0`, amount, uid, amount)\n\tif err == nil && affected == 0 {\n\t\treturn fmt.Errorf(\"update error, maybe balance not enough\")\n\t}\n\treturn err\n}\n\nfunc tccAdjustBalance(db dtmcli.DB, uid int, amount int) error {\n\taffected, err := dtmimp.DBExec(BusiConf.Driver, db, `update dtm_busi.user_account\n\t\tset trading_balance=trading_balance-?,\n\t\tbalance=balance+? where user_id=?`, amount, amount, uid)\n\tif err == nil && affected == 0 {\n\t\treturn fmt.Errorf(\"update user_account 0 rows\")\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "test/busi/quick_start.go",
    "content": "package busi\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/lithammer/shortuuid/v3\"\n)\n\n// busi address\nconst qsBusiAPI = \"/api/busi_start\"\nconst qsBusiPort = 8082\n\nvar qsBusi = fmt.Sprintf(\"http://localhost:%d%s\", qsBusiPort, qsBusiAPI)\n\n// QsMain will be call from dtm/qs\nfunc QsMain() {\n\tQsStartSvr()\n\tQsFireRequest()\n\tselect {}\n}\n\n// QsStartSvr quick start: start server\nfunc QsStartSvr() {\n\tapp := gin.New()\n\tqsAddRoute(app)\n\tlog.Printf(\"quick start examples listening at %d\", qsBusiPort)\n\tgo func() {\n\t\t_ = app.Run(fmt.Sprintf(\":%d\", qsBusiPort))\n\t}()\n\ttime.Sleep(100 * time.Millisecond)\n}\n\nfunc qsAddRoute(app *gin.Engine) {\n\tapp.POST(qsBusiAPI+\"/TransIn\", func(c *gin.Context) {\n\t\tlog.Printf(\"TransIn\")\n\t\tc.JSON(200, \"\")\n\t\t// c.JSON(409, \"\") // Status 409 for Failure. Won't be retried\n\t})\n\tapp.POST(qsBusiAPI+\"/TransInCompensate\", func(c *gin.Context) {\n\t\tlog.Printf(\"TransInCompensate\")\n\t\tc.JSON(200, \"\")\n\t})\n\tapp.POST(qsBusiAPI+\"/TransOut\", func(c *gin.Context) {\n\t\tlog.Printf(\"TransOut\")\n\t\tc.JSON(200, \"\")\n\t})\n\tapp.POST(qsBusiAPI+\"/TransOutCompensate\", func(c *gin.Context) {\n\t\tlog.Printf(\"TransOutCompensate\")\n\t\tc.JSON(200, \"\")\n\t})\n}\n\nconst dtmServer = \"http://localhost:36789/api/dtmsvr\"\n\n// QsFireRequest quick start: fire request\nfunc QsFireRequest() string {\n\treq := &gin.H{\"amount\": 30} // load of micro-service\n\t// DtmServer is the url of dtm\n\tsaga := dtmcli.NewSaga(dtmServer, shortuuid.New()).\n\t\t// add a TransOut sub-transaction，forward operation with url: qsBusi+\"/TransOut\", reverse compensation operation with url: qsBusi+\"/TransOutCompensate\"\n\t\tAdd(qsBusi+\"/TransOut\", qsBusi+\"/TransOutCompensate\", req).\n\t\t// add a TransIn sub-transaction, forward operation with url: qsBusi+\"/TransIn\", reverse compensation operation with url: qsBusi+\"/TransInCompensate\"\n\t\tAdd(qsBusi+\"/TransIn\", qsBusi+\"/TransInCompensate\", req)\n\t// submit the created saga transaction，dtm ensures all sub-transactions either complete or get revoked\n\terr := saga.Submit()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn saga.Gid\n}\n"
  },
  {
    "path": "test/busi/startup.go",
    "content": "package busi\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n\tgrpc \"google.golang.org/grpc\"\n)\n\n// Startup startup the busi's grpc and http service\nfunc Startup() (*gin.Engine, *grpc.Server) {\n\tsvr := GrpcStartup()\n\tapp := BaseAppStartup()\n\treturn app, svr\n}\n"
  },
  {
    "path": "test/busi/utils.go",
    "content": "package busi\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n\tsync \"sync\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgpb\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/go-resty/resty/v2\"\n\t\"github.com/redis/go-redis/v9\"\n\t\"go.mongodb.org/mongo-driver/bson\"\n\t\"go.mongodb.org/mongo-driver/mongo\"\n\t\"go.mongodb.org/mongo-driver/mongo/options\"\n\tgrpc \"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/metadata\"\n)\n\nfunc dbGet() *dtmutil.DB {\n\treturn dtmutil.DbGet(BusiConf)\n}\n\nfunc pdbGet() *sql.DB {\n\tdb, err := dtmimp.PooledDB(BusiConf)\n\tlogger.FatalIfError(err)\n\treturn db\n}\n\nfunc txGet() *sql.Tx {\n\tdb := pdbGet()\n\ttx, err := db.Begin()\n\tlogger.FatalIfError(err)\n\treturn tx\n}\n\n// ResetXaData will rollback all pending xa transaction\nfunc ResetXaData() {\n\tif BusiConf.Driver != \"mysql\" {\n\t\treturn\n\t}\n\n\tdb := dbGet()\n\ttype XaRow struct {\n\t\tData string\n\t}\n\txas := []XaRow{}\n\tdb.Must().Raw(\"xa recover\").Scan(&xas)\n\tfor _, xa := range xas {\n\t\tdb.Must().Exec(fmt.Sprintf(\"xa rollback '%s'\", xa.Data))\n\t}\n}\n\n// MustBarrierFromGin 1\nfunc MustBarrierFromGin(c *gin.Context) *dtmcli.BranchBarrier {\n\tti, err := dtmcli.BarrierFromQuery(c.Request.URL.Query())\n\tlogger.FatalIfError(err)\n\treturn ti\n}\n\n// MustBarrierFromGrpc 1\nfunc MustBarrierFromGrpc(ctx context.Context) *dtmcli.BranchBarrier {\n\tti, err := dtmgrpc.BarrierFromGrpc(ctx)\n\tlogger.FatalIfError(err)\n\treturn ti\n}\n\n// string2DtmError translate string to dtm error\nfunc string2DtmError(str string) error {\n\treturn map[string]error{\n\t\tdtmcli.ResultFailure: dtmcli.ErrFailure,\n\t\tdtmcli.ResultOngoing: dtmcli.ErrOngoing,\n\t\tdtmcli.ResultSuccess: nil,\n\t\t\"\":                   nil,\n\t}[str]\n}\n\n// SetGrpcHeaderForHeadersYes interceptor to set head for HeadersYes\nfunc SetGrpcHeaderForHeadersYes(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {\n\tif r, ok := req.(*dtmgpb.DtmRequest); ok && strings.HasSuffix(r.Gid, \"HeadersYes\") {\n\t\tlogger.Debugf(\"writing test_header:test to ctx\")\n\t\tmd := metadata.New(map[string]string{\"test_header\": \"test\"})\n\t\tctx = metadata.NewOutgoingContext(ctx, md)\n\t}\n\treturn invoker(ctx, method, req, reply, cc, opts...)\n}\n\n// SetHTTPHeaderForHeadersYes interceptor to set head for HeadersYes\nfunc SetHTTPHeaderForHeadersYes(c *resty.Client, r *resty.Request) error {\n\tif b, ok := r.Body.(*dtmimp.TransBase); ok && strings.HasSuffix(b.Gid, \"HeadersYes\") {\n\t\tlogger.Debugf(\"set test_header for url: %s\", r.URL)\n\t\tr.SetHeader(\"test_header\", \"yes\")\n\t}\n\treturn nil\n}\n\n// oldWrapHandler old wrap handler for test use of dtm\nfunc oldWrapHandler(fn func(*gin.Context) (interface{}, error)) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tbegan := time.Now()\n\t\tr, err := func() (r interface{}, rerr error) {\n\t\t\tdefer dtmimp.P2E(&rerr)\n\t\t\treturn fn(c)\n\t\t}()\n\t\tvar b = []byte{}\n\t\tif resp, ok := r.(*resty.Response); ok { // if it is a response，the get the body\n\t\t\tb = resp.Body()\n\t\t} else if err == nil {\n\t\t\tb, err = json.Marshal(r)\n\t\t}\n\n\t\tif err != nil {\n\t\t\tlogger.Errorf(\"%2dms 500 %s %s %s %s\", time.Since(began).Milliseconds(), err.Error(), c.Request.Method, c.Request.RequestURI, string(b))\n\t\t\tc.JSON(500, map[string]interface{}{\"code\": 500, \"message\": err.Error()})\n\t\t} else {\n\t\t\tlogger.Infof(\"%2dms 200 %s %s %s\", time.Since(began).Milliseconds(), c.Request.Method, c.Request.RequestURI, string(b))\n\t\t\tc.Status(200)\n\t\t\tc.Writer.Header().Add(\"Content-Type\", \"application/json\")\n\t\t\t_, err = c.Writer.Write(b)\n\t\t\tdtmimp.E2P(err)\n\t\t}\n\t}\n}\n\nvar (\n\trdb  redis.Cmdable\n\tonce sync.Once\n)\n\n// RedisGet 1\nfunc RedisGet() redis.Cmdable {\n\tonce.Do(func() {\n\t\tlogger.Debugf(\"connecting to client redis\")\n\t\trdb = redis.NewClient(&redis.Options{\n\t\t\tAddr:     fmt.Sprintf(\"%s:6379\", StoreHost),\n\t\t\tUsername: \"root\",\n\t\t\tPassword: \"\",\n\t\t})\n\t})\n\treturn rdb\n}\n\nvar (\n\tmongoOnce sync.Once\n\tmongoc    *mongo.Client\n)\n\n// MongoGet get mongo client\nfunc MongoGet() *mongo.Client {\n\tmongoOnce.Do(func() {\n\t\turi := fmt.Sprintf(\"mongodb://%s:27017/?retryWrites=false&directConnection=true\", StoreHost)\n\t\tctx := context.Background()\n\t\tlogger.Infof(\"connecting to mongo: %s\", uri)\n\t\tclient, err := mongo.Connect(ctx, options.Client().ApplyURI(uri))\n\t\tdtmimp.E2P(err)\n\t\tlogger.Infof(\"connected to mongo: %s\", uri)\n\t\tmongoc = client\n\t})\n\treturn mongoc\n}\n\n// SetRedisBothAccount 1\nfunc SetRedisBothAccount(amountA int, ammountB int) {\n\trd := RedisGet()\n\t_, err := rd.Set(context.Background(), GetRedisAccountKey(TransOutUID), amountA, 0).Result()\n\tdtmimp.E2P(err)\n\t_, err = rd.Set(context.Background(), GetRedisAccountKey(TransInUID), ammountB, 0).Result()\n\tdtmimp.E2P(err)\n}\n\n// SetMongoBothAccount 1\nfunc SetMongoBothAccount(amountA int, amountB int) {\n\tmc := MongoGet()\n\tcol := mc.Database(\"dtm_busi\").Collection(\"user_account\")\n\t_, err := col.ReplaceOne(context.Background(), bson.D{{Key: \"user_id\", Value: TransOutUID}}, bson.D{{Key: \"user_id\", Value: TransOutUID}, {Key: \"balance\", Value: float64(amountA)}}, options.Replace().SetUpsert(true))\n\tdtmimp.E2P(err)\n\t_, err = col.ReplaceOne(context.Background(), bson.D{{Key: \"user_id\", Value: TransInUID}}, bson.D{{Key: \"user_id\", Value: TransInUID}, {Key: \"balance\", Value: float64(amountB)}}, options.Replace().SetUpsert(true))\n\tdtmimp.E2P(err)\n\n}\n\n// SetupMongoBarrierAndBusi 1\nfunc SetupMongoBarrierAndBusi() {\n\tmc := MongoGet()\n\terr := mc.Database(\"dtm_busi\").Drop(context.Background())\n\tdtmimp.E2P(err)\n\terr = mc.Database(\"dtm_barrier\").Drop(context.Background())\n\tdtmimp.E2P(err)\n\tcol := mc.Database(\"dtm_barrier\").Collection(\"barrier\")\n\t_, err = col.Indexes().CreateOne(context.Background(), mongo.IndexModel{\n\t\tKeys: bson.D{\n\t\t\t{Key: \"gid\", Value: 1},\n\t\t\t{Key: \"branch_id\", Value: 1},\n\t\t\t{Key: \"op\", Value: 1},\n\t\t\t{Key: \"barrier_id\", Value: 1},\n\t\t},\n\t\tOptions: options.Index().SetUnique(true),\n\t})\n\tdtmimp.E2P(err)\n\tSetMongoBothAccount(10000, 10000)\n}\n"
  },
  {
    "path": "test/common_test.go",
    "content": "package test\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage/sql\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestGeneralDB(t *testing.T) {\n\tif conf.Store.IsDB() {\n\t\ttestSQL(t)\n\t\ttestDbAlone(t)\n\t}\n}\n\nfunc testSQL(t *testing.T) {\n\tconf := conf.Store.GetDBConf()\n\tconf.Host = \"127.0.0.1\" // use a new host to trigger SetDBConn called\n\tdb := dtmutil.DbGet(conf, sql.SetDBConn)\n\terr := func() (rerr error) {\n\t\tdefer dtmimp.P2E(&rerr)\n\t\tdb.Must().Exec(\"select a\")\n\t\treturn nil\n\t}()\n\tassert.NotEqual(t, nil, err)\n}\n\nfunc testDbAlone(t *testing.T) {\n\tdb, err := dtmimp.StandaloneDB(conf.Store.GetDBConf())\n\tassert.Nil(t, err)\n\t_, err = dtmimp.DBExec(conf.Store.Driver, db, \"select 1\")\n\tassert.Equal(t, nil, err)\n\t_, err = dtmimp.DBExec(conf.Store.Driver, db, \"\")\n\tassert.Equal(t, nil, err)\n\tdb.Close()\n\t_, err = dtmimp.DBExec(conf.Store.Driver, db, \"select 1\")\n\tassert.NotEqual(t, nil, err)\n}\n\nfunc TestMustGenGid(t *testing.T) {\n\tdtmgrpc.MustGenGid(dtmutil.DefaultGrpcServer)\n\tdtmcli.MustGenGid(dtmutil.DefaultHTTPServer)\n}\n\nfunc MaySkipMongo(t *testing.T) {\n\tif os.Getenv(\"SKIP_MONGO\") != \"\" {\n\t\tt.Skip(\"skipping test with mongo\")\n\t}\n}\n"
  },
  {
    "path": "test/dtmsvr_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgpb\"\n\t\"github.com/dtm-labs/dtm/client/workflow\"\n\t\"github.com/dtm-labs/dtm/dtmsvr\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/config\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar DtmServer = dtmutil.DefaultHTTPServer\nvar DtmGrpcServer = dtmutil.DefaultGrpcServer\nvar Busi = busi.Busi\n\nfunc getTransStatus(gid string) string {\n\treturn dtmsvr.GetTransGlobal(gid).Status\n}\n\nfunc getTrans(gid string) *dtmsvr.TransGlobal {\n\treturn dtmsvr.GetTransGlobal(gid)\n}\n\nfunc getBranchesStatus(gid string) []string {\n\tbranches := dtmsvr.GetStore().FindBranches(gid)\n\tstatus := []string{}\n\tfor _, branch := range branches {\n\t\tstatus = append(status, branch.Status)\n\t}\n\treturn status\n}\n\nfunc isSQLStore() bool {\n\treturn conf.Store.Driver == config.Mysql || conf.Store.Driver == config.Postgres\n}\nfunc TestUpdateBranchAsync(t *testing.T) {\n\tif !isSQLStore() {\n\t\treturn\n\t}\n\tconf.UpdateBranchSync = 0\n\tsaga := genSaga1(dtmimp.GetFuncName(), false, false)\n\tsaga.WaitResult = true\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(saga.Gid)\n\n\tgid := dtmimp.GetFuncName() + \"-wf\"\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolHTTP)\n\terr = workflow.Register(gid, func(wf *workflow.Workflow, data []byte) error {\n\t\t_, err := busi.BusiCli.TransOut(wf.NewBranchCtx(), &busi.ReqGrpc{})\n\t\t// add additional data directly\n\t\tdtmimp.TransRegisterBranch(wf.TransBase, map[string]string{\n\t\t\t\"branch_id\": \"01\",\n\t\t\t\"op\":        \"action\",\n\t\t\t\"status\":    \"succeed\",\n\t\t}, \"registerBranch\")\n\t\treturn err\n\t})\n\tassert.Nil(t, err)\n\terr = workflow.Execute(gid, gid, nil)\n\tassert.Nil(t, err)\n\n\ttime.Sleep(dtmsvr.UpdateBranchAsyncInterval)\n\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n\n\tassert.Equal(t, []string{StatusSucceed}, getBranchesStatus(gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\n\tconf.UpdateBranchSync = 1\n}\n\nfunc TestGrpcPanic(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\treq := dtmgpb.DtmRequest{\n\t\tGid: gid,\n\t}\n\terr := dtmgimp.MustGetGrpcConn(DtmGrpcServer, false).Invoke(context.Background(), \"/dtmgimp.Dtm/\"+\"Submit\", &req, nil)\n\tassert.Error(t, err)\n}\n"
  },
  {
    "path": "test/main_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/workflow\"\n\t\"github.com/dtm-labs/dtm/dtmsvr\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/config\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage/registry\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/dtm-labs/dtmdriver\"\n\t\"github.com/dtm-labs/logger\"\n)\n\nfunc TestMain(m *testing.M) {\n\tconfig.MustLoadConfig(\"\")\n\tlogger.InitLog(\"debug\")\n\tdtmsvr.TransProcessedTestChan = make(chan string, 1)\n\tdtmsvr.NowForwardDuration = 0 * time.Second\n\tdtmsvr.CronForwardDuration = 180 * time.Second\n\tconf.UpdateBranchSync = 1\n\tconf.ConfigUpdateInterval = 1\n\tconf.AlertWebHook = busi.Busi + \"/AlertWebHook\"\n\n\tdtmdriver.Middlewares.HTTP = append(dtmdriver.Middlewares.HTTP, busi.SetHTTPHeaderForHeadersYes)\n\tdtmdriver.Middlewares.Grpc = append(dtmdriver.Middlewares.Grpc, busi.SetGrpcHeaderForHeadersYes)\n\n\ttenv := dtmimp.OrString(os.Getenv(\"TEST_STORE\"), config.Redis)\n\tconf.ConfigUpdateInterval = 1\n\tconf.Store.Host = \"localhost\"\n\tconf.Store.Driver = tenv\n\tif tenv == \"boltdb\" {\n\t} else if tenv == config.Mysql {\n\t\tconf.Store.Port = 3306\n\t\tconf.Store.User = \"root\"\n\t\tconf.Store.Password = \"\"\n\t} else if tenv == config.Postgres {\n\t\tconf.Store.Port = 5432\n\t\tconf.Store.User = \"postgres\"\n\t\tconf.Store.Password = \"mysecretpassword\"\n\t} else if tenv == config.Redis {\n\t\tconf.Store.User = \"\"\n\t\tconf.Store.Password = \"\"\n\t\tconf.Store.Port = 6379\n\t} else if tenv == config.SQLServer {\n\t\tconf.Store.User = \"sa\"\n\t\tconf.Store.Password = \"p@ssw0rd\"\n\t\tconf.Store.Port = 1433\n\t}\n\tconf.Store.Db = \"\"\n\tregistry.WaitStoreUp()\n\n\tdtmsvr.PopulateDB(false)\n\tconf.Store.Db = \"dtm\" // after populateDB, set current db to dtm\n\tif tenv == \"postgres\" {\n\t\tbusi.BusiConf = conf.Store.GetDBConf()\n\t\tdtmcli.SetCurrentDBType(tenv)\n\t}\n\tgo dtmsvr.StartSvr()\n\n\tbusi.PopulateDB(false)\n\thsvr, gsvr := busi.Startup()\n\t// WorkflowStarup 1\n\tworkflow.InitHTTP(dtmutil.DefaultHTTPServer, Busi+\"/workflow/resume\")\n\tworkflow.InitGrpc(dtmutil.DefaultGrpcServer, busi.BusiGrpc, gsvr)\n\tgo busi.RunGrpc(gsvr)\n\tgo busi.RunHTTP(hsvr)\n\n\tsubscribeTopic()\n\tsubscribeGrpcTopic()\n\tdtmsvr.CronUpdateTopicsMapOnce()\n\tlogger.Debugf(\"unit main test inited\")\n\tr := m.Run()\n\tif r != 0 {\n\t\tos.Exit(r)\n\t}\n\tclose(dtmsvr.TransProcessedTestChan)\n\tgid, more := <-dtmsvr.TransProcessedTestChan\n\tlogger.FatalfIf(more, \"extra gid: %s in test chan\", gid)\n\tos.Exit(0)\n}\n"
  },
  {
    "path": "test/msg_barrier_mongo_test.go",
    "content": "package test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.mongodb.org/mongo-driver/mongo\"\n)\n\nfunc TestMsgMongoDoSucceed(t *testing.T) {\n\tMaySkipMongo(t)\n\tbefore := getBeforeBalances(\"mongo\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(DtmServer, gid).\n\t\tAdd(busi.Busi+\"/SagaMongoTransIn\", req)\n\terr := msg.DoAndSubmit(Busi+\"/MongoQueryPrepared\", func(bb *dtmcli.BranchBarrier) error {\n\t\treturn bb.MongoCall(busi.MongoGet(), func(sc mongo.SessionContext) error {\n\t\t\treturn busi.SagaMongoAdjustBalance(sc, sc.Client(), busi.TransOutUID, -30, \"\")\n\t\t})\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(msg.Gid)\n\tassert.Equal(t, []string{StatusSucceed}, getBranchesStatus(msg.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n\tassertNotSameBalance(t, before, \"mongo\")\n}\n\nfunc TestMsgMongoDoBusiFailed(t *testing.T) {\n\tMaySkipMongo(t)\n\tbefore := getBeforeBalances(\"mongo\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(DtmServer, gid).\n\t\tAdd(busi.Busi+\"/SagaMongoTransIn\", req)\n\terr := msg.DoAndSubmit(Busi+\"/MongoQueryPrepared\", func(bb *dtmcli.BranchBarrier) error {\n\t\treturn errors.New(\"an error\")\n\t})\n\tassert.Error(t, err)\n\tassertSameBalance(t, before, \"mongo\")\n}\n\nfunc TestMsgMongoDoBusiLater(t *testing.T) {\n\tMaySkipMongo(t)\n\tbefore := getBeforeBalances(\"mongo\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\t_, err := dtmcli.GetRestyClient().R().\n\t\tSetQueryParams(map[string]string{\n\t\t\t\"trans_type\": \"msg\",\n\t\t\t\"gid\":        gid,\n\t\t\t\"branch_id\":  dtmimp.MsgDoBranch0,\n\t\t\t\"op\":         dtmimp.MsgDoOp,\n\t\t\t\"barrier_id\": dtmimp.MsgDoBarrier1,\n\t\t}).\n\t\tSetBody(req).Get(Busi + \"/MongoQueryPrepared\")\n\tassert.Nil(t, err)\n\tmsg := dtmcli.NewMsg(DtmServer, gid).\n\t\tAdd(busi.Busi+\"/SagaMongoTransIn\", req)\n\terr = msg.DoAndSubmit(Busi+\"/MongoQueryPrepared\", func(bb *dtmcli.BranchBarrier) error {\n\t\treturn bb.MongoCall(busi.MongoGet(), func(sc mongo.SessionContext) error {\n\t\t\treturn busi.SagaMongoAdjustBalance(sc, sc.Client(), busi.TransOutUID, -30, \"\")\n\t\t})\n\t})\n\tassert.Error(t, err, dtmcli.ErrDuplicated)\n\tassertSameBalance(t, before, \"mongo\")\n}\n\nfunc TestMsgMongoDoCommitFailed(t *testing.T) {\n\tMaySkipMongo(t)\n\tbefore := getBeforeBalances(\"mongo\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(DtmServer, gid).\n\t\tAdd(busi.Busi+\"/SagaMongoTransIn\", req)\n\terr := msg.DoAndSubmit(Busi+\"/MongoQueryPrepared\", func(bb *dtmcli.BranchBarrier) error {\n\t\treturn bb.MongoCall(busi.MongoGet(), func(sc mongo.SessionContext) error {\n\t\t\terr := busi.SagaMongoAdjustBalance(sc, sc.Client(), busi.TransOutUID, -30, \"\")\n\t\t\tassert.Nil(t, err)\n\t\t\treturn errors.New(\"commit failed\")\n\t\t})\n\t})\n\tassert.Error(t, err)\n\tassertSameBalance(t, before, \"mongo\")\n}\n\nfunc TestMsgMongoDoCommitAfterFailed(t *testing.T) {\n\tMaySkipMongo(t)\n\tbefore := getBeforeBalances(\"mongo\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(DtmServer, gid).\n\t\tAdd(busi.Busi+\"/SagaMongoTransIn\", req)\n\terr := msg.DoAndSubmit(Busi+\"/MongoQueryPrepared\", func(bb *dtmcli.BranchBarrier) error {\n\t\terr := bb.MongoCall(busi.MongoGet(), func(sc mongo.SessionContext) error {\n\t\t\treturn busi.SagaMongoAdjustBalance(sc, sc.Client(), busi.TransOutUID, -30, \"\")\n\t\t})\n\t\tassert.Nil(t, err)\n\t\treturn errors.New(\"an error\")\n\t})\n\tassert.Error(t, err)\n\twaitTransProcessed(gid)\n\tassertNotSameBalance(t, before, \"mongo\")\n}\n"
  },
  {
    "path": "test/msg_barrier_redis_test.go",
    "content": "package test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestMsgRedisDo(t *testing.T) {\n\tbefore := getBeforeBalances(\"redis\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(DtmServer, gid).\n\t\tAdd(busi.Busi+\"/SagaRedisTransIn\", req)\n\terr := msg.DoAndSubmit(Busi+\"/RedisQueryPrepared\", func(bb *dtmcli.BranchBarrier) error {\n\t\treturn bb.RedisCheckAdjustAmount(busi.RedisGet(), busi.GetRedisAccountKey(busi.TransOutUID), -30, 86400)\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(msg.Gid)\n\tassert.Equal(t, []string{StatusSucceed}, getBranchesStatus(msg.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n\tassertNotSameBalance(t, before, \"redis\")\n}\n\nfunc TestMsgRedisDoBusiFailed(t *testing.T) {\n\tbefore := getBeforeBalances(\"redis\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(DtmServer, gid).\n\t\tAdd(busi.Busi+\"/SagaRedisTransIn\", req)\n\terr := msg.DoAndSubmit(Busi+\"/RedisQueryPrepared\", func(bb *dtmcli.BranchBarrier) error {\n\t\treturn errors.New(\"an error\")\n\t})\n\tassert.Error(t, err)\n\tassertSameBalance(t, before, \"redis\")\n}\n\nfunc TestMsgRedisDoBusiLater(t *testing.T) {\n\tbefore := getBeforeBalances(\"redis\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\t_, err := dtmcli.GetRestyClient().R().\n\t\tSetQueryParams(map[string]string{\n\t\t\t\"trans_type\": \"msg\",\n\t\t\t\"gid\":        gid,\n\t\t\t\"branch_id\":  dtmimp.MsgDoBranch0,\n\t\t\t\"op\":         dtmimp.MsgDoOp,\n\t\t\t\"barrier_id\": dtmimp.MsgDoBarrier1,\n\t\t}).\n\t\tSetBody(req).Get(Busi + \"/RedisQueryPrepared\")\n\tassert.Nil(t, err)\n\tmsg := dtmcli.NewMsg(DtmServer, gid).\n\t\tAdd(busi.Busi+\"/SagaRedisTransIn\", req)\n\terr = msg.DoAndSubmit(Busi+\"/RedisQueryPrepared\", func(bb *dtmcli.BranchBarrier) error {\n\t\treturn bb.RedisCheckAdjustAmount(busi.RedisGet(), busi.GetRedisAccountKey(busi.TransOutUID), -30, 86400)\n\t})\n\tassert.Error(t, err, dtmcli.ErrDuplicated)\n\tassertSameBalance(t, before, \"redis\")\n}\n\nfunc TestMsgRedisDoPrepareFailed(t *testing.T) {\n\tbefore := getBeforeBalances(\"redis\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(DtmServer+\"not-exists\", gid).\n\t\tAdd(busi.Busi+\"/SagaRedisTransIn\", req)\n\terr := msg.DoAndSubmit(Busi+\"/RedisQueryPrepared\", func(bb *dtmcli.BranchBarrier) error {\n\t\treturn bb.RedisCheckAdjustAmount(busi.RedisGet(), busi.GetRedisAccountKey(busi.TransOutUID), -30, 86400)\n\t})\n\tassert.Error(t, err)\n\tassertSameBalance(t, before, \"redis\")\n}\n\nfunc TestMsgRedisDoCommitFailed(t *testing.T) {\n\tbefore := getBeforeBalances(\"redis\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(DtmServer, gid).\n\t\tAdd(busi.Busi+\"/SagaRedisTransIn\", req)\n\terr := msg.DoAndSubmit(Busi+\"/RedisQueryPrepared\", func(bb *dtmcli.BranchBarrier) error {\n\t\treturn errors.New(\"after commit error\")\n\t})\n\tassert.Error(t, err)\n\tassertSameBalance(t, before, \"redis\")\n}\n\nfunc TestMsgRedisDoCommitAfterFailed(t *testing.T) {\n\tbefore := getBeforeBalances(\"redis\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(DtmServer, gid).\n\t\tAdd(busi.Busi+\"/SagaRedisTransIn\", req)\n\terr := msg.DoAndSubmit(Busi+\"/RedisQueryPrepared\", func(bb *dtmcli.BranchBarrier) error {\n\t\terr := bb.RedisCheckAdjustAmount(busi.RedisGet(), busi.GetRedisAccountKey(busi.TransOutUID), -30, 86400)\n\t\tdtmimp.E2P(err)\n\t\treturn errors.New(\"an error\")\n\t})\n\tassert.Error(t, err)\n\twaitTransProcessed(gid)\n\tassertNotSameBalance(t, before, \"redis\")\n}\n"
  },
  {
    "path": "test/msg_barrier_test.go",
    "content": "package test\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/agiledragon/gomonkey/v2\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestMsgDoAndSubmit(t *testing.T) {\n\tbefore := getBeforeBalances(\"mysql\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(DtmServer, gid).\n\t\tAdd(busi.Busi+\"/SagaBTransIn\", req)\n\terr := msg.DoAndSubmitDB(Busi+\"/QueryPreparedB\", dbGet().ToSQLDB(), func(tx *sql.Tx) error {\n\t\treturn busi.SagaAdjustBalance(tx, busi.TransOutUID, -req.Amount, \"SUCCESS\")\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(msg.Gid)\n\tassert.Equal(t, []string{StatusSucceed}, getBranchesStatus(msg.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n\tassertNotSameBalance(t, before, \"mysql\")\n}\n\nfunc TestMsgDoAndSubmitBusiFailed(t *testing.T) {\n\tbefore := getBeforeBalances(\"mysql\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(DtmServer, gid).\n\t\tAdd(busi.Busi+\"/SagaBTransIn\", req)\n\terr := msg.DoAndSubmitDB(Busi+\"/QueryPreparedB\", dbGet().ToSQLDB(), func(tx *sql.Tx) error {\n\t\treturn errors.New(\"an error\")\n\t})\n\tassert.Error(t, err)\n\tassertSameBalance(t, before, \"mysql\")\n}\n\nfunc TestMsgDoAndSubmitBusiLater(t *testing.T) {\n\tbefore := getBeforeBalances(\"mysql\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\t_, err := dtmcli.GetRestyClient().R().\n\t\tSetQueryParams(map[string]string{\n\t\t\t\"trans_type\": \"msg\",\n\t\t\t\"gid\":        gid,\n\t\t\t\"branch_id\":  dtmimp.MsgDoBranch0,\n\t\t\t\"op\":         dtmimp.MsgDoOp,\n\t\t\t\"barrier_id\": dtmimp.MsgDoBarrier1,\n\t\t}).\n\t\tSetBody(req).Get(Busi + \"/QueryPreparedB\")\n\tassert.Nil(t, err)\n\tmsg := dtmcli.NewMsg(DtmServer, gid).\n\t\tAdd(busi.Busi+\"/SagaBTransIn\", req)\n\terr = msg.DoAndSubmitDB(Busi+\"/QueryPreparedB\", dbGet().ToSQLDB(), func(tx *sql.Tx) error {\n\t\treturn nil\n\t})\n\tassert.Error(t, err, dtmcli.ErrDuplicated)\n\tassertSameBalance(t, before, \"mysql\")\n}\n\nfunc TestMsgDoAndSubmitPrepareFailed(t *testing.T) {\n\tbefore := getBeforeBalances(\"mysql\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(DtmServer+\"not-exists\", gid).\n\t\tAdd(busi.Busi+\"/SagaBTransIn\", req)\n\terr := msg.DoAndSubmitDB(Busi+\"/QueryPreparedB\", dbGet().ToSQLDB(), func(tx *sql.Tx) error {\n\t\treturn busi.SagaAdjustBalance(tx, busi.TransOutUID, -req.Amount, \"SUCCESS\")\n\t})\n\tassert.Error(t, err)\n\tassertSameBalance(t, before, \"mysql\")\n}\n\nfunc TestMsgDoAndSubmitCommitFailed(t *testing.T) {\n\tif conf.Store.IsDB() { // cannot patch tx.Commit, because Prepare also do Commit\n\t\treturn\n\t}\n\tbefore := getBeforeBalances(\"mysql\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(DtmServer, gid).\n\t\tAdd(busi.Busi+\"/SagaBTransIn\", req)\n\tvar g *gomonkey.Patches\n\terr := msg.DoAndSubmitDB(Busi+\"/QueryPreparedB\", dbGet().ToSQLDB(), func(tx *sql.Tx) error {\n\t\tg = gomonkey.ApplyMethod(reflect.TypeOf(tx), \"Commit\", func(tx *sql.Tx) error {\n\t\t\tlogger.Debugf(\"tx.Commit rollback and return error in test\")\n\t\t\t_ = tx.Rollback()\n\t\t\treturn errors.New(\"test error for patch\")\n\t\t})\n\t\treturn busi.SagaAdjustBalance(tx, busi.TransOutUID, -req.Amount, \"SUCCESS\")\n\t})\n\tg.Reset()\n\tassert.Error(t, err)\n\tassertSameBalance(t, before, \"mysql\")\n}\n\nfunc TestMsgDoAndSubmitCommitAfterFailed(t *testing.T) {\n\tif conf.Store.IsDB() { // cannot patch tx.Commit, because Prepare also do Commit\n\t\treturn\n\t}\n\tbefore := getBeforeBalances(\"mysql\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(DtmServer, gid).\n\t\tAdd(busi.Busi+\"/SagaBTransIn\", req)\n\tvar guard *gomonkey.Patches\n\terr := msg.DoAndSubmitDB(Busi+\"/QueryPreparedB\", dbGet().ToSQLDB(), func(tx *sql.Tx) error {\n\t\terr := busi.SagaAdjustBalance(tx, busi.TransOutUID, -req.Amount, \"SUCCESS\")\n\t\tguard = gomonkey.ApplyMethod(reflect.TypeOf(tx), \"Commit\", func(tx *sql.Tx) error {\n\t\t\tguard.Reset()\n\t\t\t_ = tx.Commit()\n\t\t\treturn errors.New(\"test error for patch\")\n\t\t})\n\t\treturn err\n\t})\n\tassert.Error(t, err)\n\twaitTransProcessed(gid)\n\tassertNotSameBalance(t, before, \"mysql\")\n}\n"
  },
  {
    "path": "test/msg_delay_test.go",
    "content": "package test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmsvr\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc genMsgDelay(gid string) *dtmcli.Msg {\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(dtmutil.DefaultHTTPServer, gid).\n\t\tAdd(busi.Busi+\"/TransOut\", &req).\n\t\tAdd(busi.Busi+\"/TransIn\", &req).SetDelay(10)\n\tmsg.QueryPrepared = busi.Busi + \"/QueryPrepared\"\n\treturn msg\n}\n\nfunc TestMsgDelayNormal(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tmsg := genMsgDelay(gid)\n\tsubmitForwardCron(0, func() {\n\t\tmsg.Submit()\n\t\twaitTransProcessed(msg.Gid)\n\t})\n\n\tdtmsvr.NowForwardDuration = 0\n\tassert.Equal(t, []string{StatusPrepared, StatusPrepared}, getBranchesStatus(msg.Gid))\n\tassert.Equal(t, StatusSubmitted, getTransStatus(msg.Gid))\n\tcronTransOnceForwardCron(t, \"\", 0)\n\tcronTransOnceForwardCron(t, \"\", 8)\n\tcronTransOnceForwardCron(t, gid, 12)\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed}, getBranchesStatus(msg.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n}\n"
  },
  {
    "path": "test/msg_grpc_barrier_redis_test.go",
    "content": "package test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestMsgGrpcRedisDo(t *testing.T) {\n\tbefore := getBeforeBalances(\"redis\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqGrpc(30, false, false)\n\tmsg := dtmgrpc.NewMsgGrpc(DtmGrpcServer, gid).\n\t\tAdd(busi.BusiGrpc+\"/busi.Busi/TransInRedis\", req)\n\terr := msg.DoAndSubmit(busi.BusiGrpc+\"/busi.Busi/QueryPreparedRedis\", func(bb *dtmcli.BranchBarrier) error {\n\t\treturn bb.RedisCheckAdjustAmount(busi.RedisGet(), busi.GetRedisAccountKey(busi.TransOutUID), -30, 86400)\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(msg.Gid)\n\tassert.Equal(t, []string{StatusSucceed}, getBranchesStatus(msg.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n\tassertNotSameBalance(t, before, \"redis\")\n}\n\nfunc TestMsgGrpcRedisDoBusiFailed(t *testing.T) {\n\tbefore := getBeforeBalances(\"redis\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqGrpc(30, false, false)\n\tmsg := dtmgrpc.NewMsgGrpc(DtmGrpcServer, gid).\n\t\tAdd(busi.BusiGrpc+\"/busi.Busi/TransInRedis\", req)\n\terr := msg.DoAndSubmit(busi.BusiGrpc+\"/busi.Busi/QueryPreparedRedis\", func(bb *dtmcli.BranchBarrier) error {\n\t\treturn errors.New(\"an error\")\n\t})\n\tassert.Error(t, err)\n\tassertSameBalance(t, before, \"redis\")\n}\n\nfunc TestMsgGrpcRedisDoPrepareFailed(t *testing.T) {\n\tbefore := getBeforeBalances(\"redis\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqGrpc(30, false, false)\n\tmsg := dtmgrpc.NewMsgGrpc(DtmGrpcServer+\"not-exists\", gid).\n\t\tAdd(busi.BusiGrpc+\"/busi.Busi/TransInRedis\", req)\n\terr := msg.DoAndSubmit(busi.BusiGrpc+\"/busi.Busi/QueryPreparedRedis\", func(bb *dtmcli.BranchBarrier) error {\n\t\treturn bb.RedisCheckAdjustAmount(busi.RedisGet(), busi.GetRedisAccountKey(busi.TransOutUID), -30, 86400)\n\t})\n\tassert.Error(t, err)\n\tassertSameBalance(t, before, \"redis\")\n}\n\nfunc TestMsgGrpcRedisDoCommitFailed(t *testing.T) {\n\tbefore := getBeforeBalances(\"redis\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqGrpc(30, false, false)\n\tmsg := dtmgrpc.NewMsgGrpc(DtmGrpcServer, gid).\n\t\tAdd(busi.BusiGrpc+\"/busi.Busi/TransInRedis\", req)\n\terr := msg.DoAndSubmit(busi.BusiGrpc+\"/busi.Busi/QueryPreparedRedis\", func(bb *dtmcli.BranchBarrier) error {\n\t\treturn errors.New(\"after commit error\")\n\t})\n\tassert.Error(t, err)\n\tassertSameBalance(t, before, \"redis\")\n}\n\nfunc TestMsgGrpcRedisDoCommitAfterFailed(t *testing.T) {\n\tbefore := getBeforeBalances(\"redis\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqGrpc(30, false, false)\n\tmsg := dtmgrpc.NewMsgGrpc(DtmGrpcServer, gid).\n\t\tAdd(busi.BusiGrpc+\"/busi.Busi/TransInRedis\", req)\n\terr := msg.DoAndSubmit(busi.BusiGrpc+\"/busi.Busi/QueryPreparedRedis\", func(bb *dtmcli.BranchBarrier) error {\n\t\terr := bb.RedisCheckAdjustAmount(busi.RedisGet(), busi.GetRedisAccountKey(busi.TransOutUID), -30, 86400)\n\t\tdtmimp.E2P(err)\n\t\treturn errors.New(\"an error\")\n\t})\n\tassert.Error(t, err)\n\twaitTransProcessed(gid)\n\tassertNotSameBalance(t, before, \"redis\")\n}\n"
  },
  {
    "path": "test/msg_grpc_barrier_test.go",
    "content": "package test\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/agiledragon/gomonkey/v2\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestMsgGrpcPrepareAndSubmit(t *testing.T) {\n\tbefore := getBeforeBalances(\"mysql\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqGrpc(30, false, false)\n\tmsg := dtmgrpc.NewMsgGrpc(DtmGrpcServer, gid).\n\t\tAdd(busi.BusiGrpc+\"/busi.Busi/TransInBSaga\", req)\n\terr := msg.DoAndSubmitDB(busi.BusiGrpc+\"/busi.Busi/QueryPreparedB\", dbGet().ToSQLDB(), func(tx *sql.Tx) error {\n\t\treturn busi.SagaAdjustBalance(tx, busi.TransOutUID, -int(req.Amount), \"SUCCESS\")\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(msg.Gid)\n\tassert.Equal(t, []string{StatusSucceed}, getBranchesStatus(msg.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n\tassertNotSameBalance(t, before, \"mysql\")\n}\n\nfunc TestMsgGrpcPrepareAndSubmitCommitAfterFailed(t *testing.T) {\n\tif conf.Store.IsDB() { // cannot patch tx.Commit, because Prepare also do Commit\n\t\treturn\n\t}\n\tbefore := getBeforeBalances(\"mysql\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqGrpc(30, false, false)\n\tmsg := dtmgrpc.NewMsgGrpc(DtmGrpcServer, gid).\n\t\tAdd(busi.BusiGrpc+\"/busi.Busi/TransInBSaga\", req)\n\tvar guard *gomonkey.Patches\n\terr := msg.DoAndSubmitDB(busi.BusiGrpc+\"/busi.Busi/QueryPreparedB\", dbGet().ToSQLDB(), func(tx *sql.Tx) error {\n\t\terr := busi.SagaAdjustBalance(tx, busi.TransOutUID, -int(req.Amount), \"SUCCESS\")\n\t\tguard = gomonkey.ApplyMethod(reflect.TypeOf(tx), \"Commit\", func(tx *sql.Tx) error {\n\t\t\tguard.Reset()\n\t\t\t_ = tx.Commit()\n\t\t\treturn errors.New(\"test error for patch\")\n\t\t})\n\t\treturn err\n\t})\n\tassert.Error(t, err)\n\twaitTransProcessed(gid)\n\tassertNotSameBalance(t, before, \"mysql\")\n}\n\nfunc TestMsgGrpcPrepareAndSubmitCommitFailed(t *testing.T) {\n\tif conf.Store.IsDB() { // cannot patch tx.Commit, because Prepare also do Commit\n\t\treturn\n\t}\n\tbefore := getBeforeBalances(\"mysql\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqGrpc(30, false, false)\n\tmsg := dtmgrpc.NewMsgGrpc(DtmGrpcServer, gid).\n\t\tAdd(busi.Busi+\"/SagaBTransIn\", req)\n\tvar g *gomonkey.Patches\n\terr := msg.DoAndSubmitDB(busi.BusiGrpc+\"/busi.Busi/QueryPreparedB\", dbGet().ToSQLDB(), func(tx *sql.Tx) error {\n\t\tg = gomonkey.ApplyMethod(reflect.TypeOf(tx), \"Commit\", func(tx *sql.Tx) error {\n\t\t\tlogger.Debugf(\"tx.Commit rollback and return error in test\")\n\t\t\t_ = tx.Rollback()\n\t\t\treturn errors.New(\"test error for patch\")\n\t\t})\n\t\treturn busi.SagaAdjustBalance(tx, busi.TransOutUID, -int(req.Amount), \"SUCCESS\")\n\t})\n\tg.Reset()\n\tassert.Error(t, err)\n\tassertSameBalance(t, before, \"mysql\")\n}\n"
  },
  {
    "path": "test/msg_grpc_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestMsgGrpcNormal(t *testing.T) {\n\tmsg := genGrpcMsg(dtmimp.GetFuncName())\n\terr := msg.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(msg.Gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed}, getBranchesStatus(msg.Gid))\n}\n\nfunc TestMsgGrpcTimeoutSuccess(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tmsg := genGrpcMsg(gid)\n\terr := msg.Prepare(\"\")\n\tassert.Nil(t, err)\n\tbusi.MainSwitch.QueryPreparedResult.SetOnce(dtmcli.ResultOngoing)\n\tcronTransOnceForwardNow(t, gid, 180)\n\tassert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))\n\tbusi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)\n\tcronTransOnceForwardNow(t, gid, 180)\n\tassert.Equal(t, StatusSubmitted, getTransStatus(msg.Gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusPrepared}, getBranchesStatus(msg.Gid))\n\tcronTransOnce(t, gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed}, getBranchesStatus(msg.Gid))\n}\n\nfunc TestMsgGrpcTimeoutFailed(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tmsg := genGrpcMsg(gid)\n\tmsg.Prepare(\"\")\n\tassert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))\n\tbusi.MainSwitch.QueryPreparedResult.SetOnce(dtmcli.ResultOngoing)\n\tcronTransOnceForwardNow(t, gid, 180)\n\tassert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))\n\tbusi.MainSwitch.QueryPreparedResult.SetOnce(dtmcli.ResultFailure)\n\tcronTransOnceForwardNow(t, gid, 180)\n\tassert.Equal(t, StatusFailed, getTransStatus(msg.Gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusPrepared}, getBranchesStatus(msg.Gid))\n}\n\nfunc genGrpcMsg(gid string) *dtmgrpc.MsgGrpc {\n\treq := &busi.ReqGrpc{Amount: 30}\n\tmsg := dtmgrpc.NewMsgGrpc(dtmutil.DefaultGrpcServer, gid).\n\t\tAddTopic(\"grpc_trans\", req)\n\tmsg.QueryPrepared = fmt.Sprintf(\"%s/busi.Busi/QueryPrepared\", busi.BusiGrpc)\n\treturn msg\n}\n\nfunc subscribeGrpcTopic() {\n\te2p(grpcSubscribe(\"grpc_trans\", busi.BusiGrpc+\"/busi.Busi/TransOut\"))\n\te2p(grpcSubscribe(\"grpc_trans\", busi.BusiGrpc+\"/busi.Busi/TransIn\"))\n}\n"
  },
  {
    "path": "test/msg_jrpc_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestMsgJrpcNormal(t *testing.T) {\n\tmsg := genJrpcMsg(dtmimp.GetFuncName())\n\tmsg.Submit()\n\twaitTransProcessed(msg.Gid)\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed}, getBranchesStatus(msg.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n}\n\nfunc TestMsgJrpcResults(t *testing.T) {\n\tmsg := genJrpcMsg(dtmimp.GetFuncName())\n\tbusi.MainSwitch.JrpcResult.SetOnce(\"OTHER\")\n\terr := msg.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(msg.Gid)\n\tassert.Equal(t, StatusSubmitted, getTransStatus(msg.Gid))\n\tbusi.MainSwitch.JrpcResult.SetOnce(\"ONGOING\")\n\tcronTransOnceForwardNow(t, msg.Gid, 180)\n\tassert.Equal(t, StatusSubmitted, getTransStatus(msg.Gid))\n\tbusi.MainSwitch.JrpcResult.SetOnce(\"FAILURE\")\n\tcronTransOnceForwardNow(t, msg.Gid, 180)\n\tassert.Equal(t, StatusSubmitted, getTransStatus(msg.Gid))\n\n\tcronTransOnceForwardNow(t, msg.Gid, 180)\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed}, getBranchesStatus(msg.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n}\n\nfunc TestMsgJrpcDoAndSubmit(t *testing.T) {\n\tbefore := getBeforeBalances(\"mysql\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(dtmutil.DefaultJrpcServer, gid).\n\t\tAdd(busi.Busi+\"/SagaBTransIn\", req)\n\tmsg.Protocol = dtmimp.Jrpc\n\terr := msg.DoAndSubmitDB(Busi+\"/QueryPreparedB\", dbGet().ToSQLDB(), func(tx *sql.Tx) error {\n\t\treturn busi.SagaAdjustBalance(tx, busi.TransOutUID, -req.Amount, \"SUCCESS\")\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(msg.Gid)\n\tassert.Equal(t, []string{StatusSucceed}, getBranchesStatus(msg.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n\tassertNotSameBalance(t, before, \"mysql\")\n}\n\nfunc TestMsgJrpcDoAndSubmitBusiFailed(t *testing.T) {\n\tbefore := getBeforeBalances(\"mysql\")\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(dtmutil.DefaultJrpcServer, gid).\n\t\tAdd(busi.Busi+\"/SagaBTransIn\", req)\n\tmsg.Protocol = dtmimp.Jrpc\n\terr := msg.DoAndSubmitDB(Busi+\"/QueryPreparedB\", dbGet().ToSQLDB(), func(tx *sql.Tx) error {\n\t\treturn errors.New(\"an error\")\n\t})\n\tassert.Error(t, err)\n\tassertSameBalance(t, before, \"mysql\")\n}\n\nfunc TestMsgJrpcRepeated(t *testing.T) {\n\tmsg := genJrpcMsg(dtmimp.GetFuncName())\n\tmsg.Submit()\n\twaitTransProcessed(msg.Gid)\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed}, getBranchesStatus(msg.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n\terr := msg.Submit()\n\tassert.Error(t, err)\n}\n\nfunc TestMsgJprcAbnormal(t *testing.T) {\n\tid := \"no-use\"\n\tresp, err := dtmcli.GetRestyClient().R().SetBody(\"hello\").Post(dtmutil.DefaultJrpcServer)\n\tassert.Nil(t, err)\n\tassert.Contains(t, resp.String(), \"-32700\")\n\n\t_, err = dtmcli.GetRestyClient().R().SetBody(\"hello\").Post(\"http://localhost:1001\")\n\tassert.Error(t, err)\n\n\tresp, err = dtmcli.GetRestyClient().R().SetBody(map[string]string{\n\t\t\"jsonrpc\": \"1.0\",\n\t\t\"method\":  \"newGid\",\n\t\t\"params\":  \"\",\n\t\t\"id\":      id,\n\t}).Post(dtmutil.DefaultJrpcServer)\n\tassert.Nil(t, err)\n\tassert.Contains(t, resp.String(), \"-32600\")\n\n\tresp, err = dtmcli.GetRestyClient().R().SetBody(map[string]string{\n\t\t\"jsonrpc\": \"2.0\",\n\t\t\"method\":  \"not-exists\",\n\t\t\"params\":  \"\",\n\t\t\"id\":      id,\n\t}).Post(dtmutil.DefaultJrpcServer)\n\tassert.Nil(t, err)\n\tassert.Contains(t, resp.String(), \"-32601\")\n\n\tresp, err = dtmcli.GetRestyClient().R().SetBody(map[string]interface{}{\n\t\t\"jsonrpc\": \"2.0\",\n\t\t\"method\":  \"registerBranch\",\n\t\t\"params\": map[string]string{\n\t\t\t\"trans_type\": \"not-exists\",\n\t\t},\n\t\t\"id\": id,\n\t}).Post(dtmutil.DefaultJrpcServer)\n\tassert.Nil(t, err)\n\tassert.Contains(t, resp.String(), \"-32603\")\n}\n\nfunc TestMsgJprcAbnormal2(t *testing.T) {\n\ttb := dtmimp.NewTransBase(dtmimp.GetFuncName(), \"msg\", dtmutil.DefaultJrpcServer, \"01\")\n\ttb.Protocol = \"json-rpc\"\n\t_, err := dtmimp.TransCallDtmExt(tb, \"\", \"newGid\")\n\tassert.Nil(t, err)\n}\n\nfunc genJrpcMsg(gid string) *dtmcli.Msg {\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(dtmutil.DefaultJrpcServer, gid).\n\t\tAdd(busi.Busi+\"/TransOut\", &req).\n\t\tAdd(busi.BusiJrpcURL+\"TransIn\", &req)\n\tmsg.QueryPrepared = busi.Busi + \"/QueryPrepared\"\n\tmsg.Protocol = dtmimp.Jrpc\n\treturn msg\n}\n"
  },
  {
    "path": "test/msg_options_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestMsgOptionsTimeout(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tmsg := genMsg(gid)\n\tmsg.Prepare(\"\")\n\tcronTransOnce(t, gid)\n\tassert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))\n\tcronTransOnceForwardNow(t, gid, 60)\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n}\n\nfunc TestMsgOptionsTimeoutCustom(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tmsg := genMsg(gid)\n\tmsg.TimeoutToFail = 120\n\tmsg.Prepare(\"\")\n\tcronTransOnce(t, gid)\n\tassert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))\n\tcronTransOnceForwardNow(t, gid, 60)\n\tassert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))\n\tcronTransOnceForwardNow(t, gid, 180)\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n}\n\nfunc TestMsgOptionsTimeoutFailed(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tmsg := genMsg(gid)\n\tmsg.TimeoutToFail = 120\n\tmsg.Prepare(\"\")\n\tcronTransOnce(t, gid)\n\tassert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))\n\tcronTransOnceForwardNow(t, gid, 60)\n\tassert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))\n\tbusi.MainSwitch.QueryPreparedResult.SetOnce(dtmcli.ResultFailure)\n\tcronTransOnceForwardNow(t, gid, 180)\n\tassert.Equal(t, StatusFailed, getTransStatus(msg.Gid))\n}\n\nfunc TestMsgConcurrent(t *testing.T) {\n\tmsg := genMsg(dtmimp.GetFuncName())\n\tmsg.Concurrent = true\n\tmsg.Submit()\n\tassert.Equal(t, StatusSubmitted, getTransStatus(msg.Gid))\n\twaitTransProcessed(msg.Gid)\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed}, getBranchesStatus(msg.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n}\n"
  },
  {
    "path": "test/msg_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestMsgNormal(t *testing.T) {\n\tmsg := genMsg(dtmimp.GetFuncName())\n\tmsg.Submit()\n\tassert.Equal(t, StatusSubmitted, getTransStatus(msg.Gid))\n\twaitTransProcessed(msg.Gid)\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed}, getBranchesStatus(msg.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n}\n\nfunc TestMsgTimeoutSuccess(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tmsg := genMsg(gid)\n\tmsg.Prepare(\"\")\n\tassert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))\n\tbusi.MainSwitch.QueryPreparedResult.SetOnce(dtmcli.ResultOngoing)\n\tcronTransOnceForwardNow(t, gid, 180)\n\tassert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))\n\tbusi.MainSwitch.TransInResult.SetOnce(dtmcli.ResultOngoing)\n\tcronTransOnceForwardNow(t, gid, 180)\n\tassert.Equal(t, StatusSubmitted, getTransStatus(msg.Gid))\n\tcronTransOnce(t, gid)\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed}, getBranchesStatus(msg.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n}\n\nfunc TestMsgTimeoutFailed(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tmsg := genMsg(gid)\n\tmsg.Prepare(\"\")\n\tassert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))\n\tbusi.MainSwitch.QueryPreparedResult.SetOnce(dtmcli.ResultOngoing)\n\tcronTransOnceForwardNow(t, gid, 360)\n\tassert.Equal(t, StatusPrepared, getTransStatus(msg.Gid))\n\tbusi.MainSwitch.QueryPreparedResult.SetOnce(dtmcli.ResultFailure)\n\tcronTransOnceForwardNow(t, gid, 180)\n\tassert.Equal(t, []string{StatusPrepared, StatusPrepared}, getBranchesStatus(msg.Gid))\n\tassert.Equal(t, StatusFailed, getTransStatus(msg.Gid))\n}\n\nfunc TestMsgAbnormal(t *testing.T) {\n\tmsg := genMsg(dtmimp.GetFuncName())\n\tmsg.Prepare(\"\")\n\terr := msg.Prepare(\"\")\n\tassert.Nil(t, err)\n\terr = msg.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(msg.Gid)\n\terr = msg.Prepare(\"\")\n\tassert.Error(t, err)\n}\n\nfunc TestMsgTopicNotFoundFailed(t *testing.T) {\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(dtmutil.DefaultHTTPServer, dtmimp.GetFuncName()).\n\t\tAddTopic(\"non_existent_topic_TestMsgTopicNotFoundFailed\", &req)\n\tmsg.QueryPrepared = busi.Busi + \"/QueryPrepared\"\n\tassert.True(t, strings.Contains(msg.Submit().Error(), \"topic not found\"))\n}\n\nfunc genMsg(gid string) *dtmcli.Msg {\n\treq := busi.GenReqHTTP(30, false, false)\n\tmsg := dtmcli.NewMsg(dtmutil.DefaultHTTPServer, gid).\n\t\tAddTopic(\"http_trans\", &req)\n\n\tmsg.QueryPrepared = busi.Busi + \"/QueryPrepared\"\n\treturn msg\n}\n\nfunc subscribeTopic() {\n\te2p(httpSubscribe(\"http_trans\", busi.Busi+\"/TransOut\"))\n\te2p(httpSubscribe(\"http_trans\", busi.Busi+\"/TransIn\"))\n}\n"
  },
  {
    "path": "test/msg_webhook_test.go",
    "content": "package test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestMsgWebhook(t *testing.T) {\n\tmsg := genMsg(dtmimp.GetFuncName())\n\tbusi.MainSwitch.TransInResult.SetOnce(\"ERROR\")\n\tmsg.Submit()\n\tassert.Equal(t, StatusSubmitted, getTransStatus(msg.Gid))\n\twaitTransProcessed(msg.Gid)\n\tbusi.MainSwitch.TransInResult.SetOnce(\"ERROR\")\n\tcronTransOnce(t, msg.Gid)\n\tbusi.MainSwitch.TransInResult.SetOnce(\"ERROR\")\n\tcronTransOnce(t, msg.Gid)\n\tassert.Equal(t, msg.Gid, busi.WebHookResult[\"gid\"])\n\tcronTransOnce(t, msg.Gid)\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed}, getBranchesStatus(msg.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n}\n\nfunc TestMsgWebhookError(t *testing.T) {\n\tmsg := genMsg(dtmimp.GetFuncName())\n\tbusi.MainSwitch.TransInResult.SetOnce(\"ERROR\")\n\tmsg.Submit()\n\tassert.Equal(t, StatusSubmitted, getTransStatus(msg.Gid))\n\twaitTransProcessed(msg.Gid)\n\tbusi.MainSwitch.TransInResult.SetOnce(\"ERROR\")\n\tcronTransOnce(t, msg.Gid)\n\tbusi.MainSwitch.TransInResult.SetOnce(\"ERROR\")\n\tcronTransOnce(t, msg.Gid)\n\tassert.Equal(t, msg.Gid, busi.WebHookResult[\"gid\"])\n\tcronTransOnce(t, msg.Gid)\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed}, getBranchesStatus(msg.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(msg.Gid))\n}\n"
  },
  {
    "path": "test/saga_barrier_mongo_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSagaBarrierMongoNormal(t *testing.T) {\n\tMaySkipMongo(t)\n\tbefore := getBeforeBalances(\"mongo\")\n\tsaga := genSagaBarrierMongo(dtmimp.GetFuncName(), false)\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n\tassertNotSameBalance(t, before, \"mongo\")\n}\n\nfunc TestSagaBarrierMongoRollback(t *testing.T) {\n\tMaySkipMongo(t)\n\tbefore := getBeforeBalances(\"mongo\")\n\tsaga := genSagaBarrierMongo(dtmimp.GetFuncName(), true)\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, StatusFailed, getTransStatus(saga.Gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(saga.Gid))\n\tassertSameBalance(t, before, \"mongo\")\n}\n\nfunc genSagaBarrierMongo(gid string, transInFailed bool) *dtmcli.Saga {\n\treq := busi.GenReqHTTP(30, false, transInFailed)\n\treq.Store = \"mongo\"\n\treturn dtmcli.NewSaga(DtmServer, gid).\n\t\tAdd(Busi+\"/SagaMongoTransOut\", Busi+\"/SagaMongoTransOutCom\", req).\n\t\tAdd(Busi+\"/SagaMongoTransIn\", Busi+\"/SagaMongoTransInCom\", req)\n}\n"
  },
  {
    "path": "test/saga_barrier_redis_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSagaBarrierRedisNormal(t *testing.T) {\n\tbusi.SetRedisBothAccount(100, 100)\n\tbefore := getBeforeBalances(\"redis\")\n\tsaga := genSagaBarrierRedis(dtmimp.GetFuncName())\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n\tassertNotSameBalance(t, before, \"redis\")\n}\n\nfunc TestSagaBarrierRedisRollback(t *testing.T) {\n\tbusi.SetRedisBothAccount(20, 20)\n\tbefore := getBeforeBalances(\"redis\")\n\tsaga := genSagaBarrierRedis(dtmimp.GetFuncName())\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, StatusFailed, getTransStatus(saga.Gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(saga.Gid))\n\tassertSameBalance(t, before, \"redis\")\n}\n\nfunc genSagaBarrierRedis(gid string) *dtmcli.Saga {\n\treq := busi.GenReqHTTP(30, false, false)\n\treq.Store = \"redis\"\n\treturn dtmcli.NewSaga(DtmServer, gid).\n\t\tAdd(Busi+\"/SagaRedisTransIn\", Busi+\"/SagaRedisTransInCom\", req).\n\t\tAdd(Busi+\"/SagaRedisTransOut\", Busi+\"/SagaRedisTransOutCom\", req)\n}\n"
  },
  {
    "path": "test/saga_barrier_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSagaBarrierNormal(t *testing.T) {\n\tsaga := genSagaBarrier(dtmimp.GetFuncName(), false, false)\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n}\n\nfunc TestSagaBarrierRollback(t *testing.T) {\n\tsaga := genSagaBarrier(dtmimp.GetFuncName(), false, true)\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, StatusFailed, getTransStatus(saga.Gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(saga.Gid))\n}\n\nfunc genSagaBarrier(gid string, outFailed, inFailed bool) *dtmcli.Saga {\n\treq := busi.GenReqHTTP(30, outFailed, inFailed)\n\treturn dtmcli.NewSaga(DtmServer, gid).\n\t\tAdd(Busi+\"/SagaBTransOut\", Busi+\"/SagaBTransOutCom\", req).\n\t\tAdd(Busi+\"/SagaBTransIn\", Busi+\"/SagaBTransInCom\", req)\n}\n\nfunc TestSagaBarrier2Normal(t *testing.T) {\n\treq := busi.GenReqHTTP(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\tsaga := dtmcli.NewSaga(DtmServer, gid).\n\t\tAdd(Busi+\"/SagaBTransOut\", Busi+\"/SagaBTransOutCom\", req).\n\t\tAdd(Busi+\"/SagaB2TransIn\", Busi+\"/SagaB2TransInCom\", req)\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n}\n"
  },
  {
    "path": "test/saga_compatible_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSagaCompatibleNormal(t *testing.T) { // compatible with old http, which put payload in steps.data\n\tgid := dtmimp.GetFuncName()\n\tbody := fmt.Sprintf(`{\"gid\":\"%s\",\"trans_type\":\"saga\",\"steps\":[{\"action\":\"%s/TransOut\",\"compensate\":\"%s/TransOutRevert\",\"data\":\"{\\\"amount\\\":30,\\\"transInResult\\\":\\\"SUCCESS\\\",\\\"transOutResult\\\":\\\"SUCCESS\\\"}\"},{\"action\":\"%s/TransIn\",\"compensate\":\"%s/TransInRevert\",\"data\":\"{\\\"amount\\\":30,\\\"transInResult\\\":\\\"SUCCESS\\\",\\\"transOutResult\\\":\\\"SUCCESS\\\"}\"}]}`,\n\t\tgid, busi.Busi, busi.Busi, busi.Busi, busi.Busi)\n\tdtmcli.GetRestyClient().R().SetBody(body).Post(fmt.Sprintf(\"%s/submit\", dtmutil.DefaultHTTPServer))\n\twaitTransProcessed(gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n}\n"
  },
  {
    "path": "test/saga_concurrent_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc genSagaCon(gid string, outFailed bool, inFailed bool) *dtmcli.Saga {\n\treturn genSaga(gid, outFailed, inFailed).SetConcurrent()\n}\n\nfunc TestSagaConNormal(t *testing.T) {\n\tsagaCon := genSagaCon(dtmimp.GetFuncName(), false, false)\n\tsagaCon.Submit()\n\twaitTransProcessed(sagaCon.Gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(sagaCon.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(sagaCon.Gid))\n}\n\nfunc TestSagaConRollbackNormal(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tsagaCon := genSagaCon(gid, true, false)\n\tbusi.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)\n\terr := sagaCon.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(sagaCon.Gid)\n\tassert.Equal(t, StatusAborting, getTransStatus(sagaCon.Gid))\n\tcronTransOnce(t, gid)\n\tassert.Equal(t, StatusFailed, getTransStatus(sagaCon.Gid))\n\t// TODO should fix this\n\t// assert.Equal(t, []string{StatusSucceed, StatusFailed, StatusSucceed, StatusSucceed}, getBranchesStatus(sagaCon.Gid))\n}\n\nfunc TestSagaConRollbackOrder(t *testing.T) {\n\tsagaCon := genSagaCon(dtmimp.GetFuncName(), true, false)\n\tsagaCon.AddBranchOrder(1, []int{0})\n\terr := sagaCon.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(sagaCon.Gid)\n\tassert.Equal(t, StatusFailed, getTransStatus(sagaCon.Gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusFailed, StatusPrepared, StatusPrepared}, getBranchesStatus(sagaCon.Gid))\n}\n\nfunc TestSagaConRollbackOrder2(t *testing.T) {\n\tsagaCon := genSagaCon(dtmimp.GetFuncName(), false, true)\n\tsagaCon.AddBranchOrder(1, []int{0})\n\terr := sagaCon.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(sagaCon.Gid)\n\tassert.Equal(t, StatusFailed, getTransStatus(sagaCon.Gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(sagaCon.Gid))\n}\nfunc TestSagaConCommittedOngoing(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tsagaCon := genSagaCon(gid, false, false)\n\tbusi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)\n\tsagaCon.Submit()\n\twaitTransProcessed(sagaCon.Gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusPrepared, StatusPrepared, StatusSucceed}, getBranchesStatus(sagaCon.Gid))\n\tassert.Equal(t, StatusSubmitted, getTransStatus(sagaCon.Gid))\n\n\tcronTransOnce(t, gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(sagaCon.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(sagaCon.Gid))\n}\n"
  },
  {
    "path": "test/saga_grpc_barrier_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSagaGrpcBarrierNormal(t *testing.T) {\n\tsaga := genSagaGrpcBarrier(dtmimp.GetFuncName(), false, false)\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n}\n\nfunc TestSagaGrpcBarrierRollback(t *testing.T) {\n\tsaga := genSagaGrpcBarrier(dtmimp.GetFuncName(), false, true)\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, StatusFailed, getTransStatus(saga.Gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(saga.Gid))\n}\n\nfunc genSagaGrpcBarrier(gid string, outFailed bool, inFailed bool) *dtmgrpc.SagaGrpc {\n\tsaga := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, gid)\n\treq := busi.GenReqGrpc(30, outFailed, inFailed)\n\tsaga.Add(busi.BusiGrpc+\"/busi.Busi/TransOutBSaga\", busi.BusiGrpc+\"/busi.Busi/TransOutRevertBSaga\", req)\n\tsaga.Add(busi.BusiGrpc+\"/busi.Busi/TransInBSaga\", busi.BusiGrpc+\"/busi.Busi/TransInRevertBSaga\", req)\n\treturn saga\n}\n"
  },
  {
    "path": "test/saga_grpc_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSagaGrpcNormal(t *testing.T) {\n\tsaga := genSagaGrpc(dtmimp.GetFuncName(), false, false)\n\tsaga.Submit()\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n}\n\nfunc TestSagaGrpcRollback(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tsaga := genSagaGrpc(gid, false, true)\n\tbusi.MainSwitch.FailureReason.SetOnce(\"Insufficient balance\")\n\tbusi.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)\n\tsaga.Submit()\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, StatusAborting, getTransStatus(saga.Gid))\n\tcronTransOnce(t, gid)\n\tassert.Equal(t, StatusFailed, getTransStatus(saga.Gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(saga.Gid))\n\tassert.Contains(t, getTrans(saga.Gid).RollbackReason, \"Insufficient balance\")\n}\n\nfunc TestSagaGrpcCurrent(t *testing.T) {\n\tsaga := genSagaGrpc(dtmimp.GetFuncName(), false, false).\n\t\tEnableConcurrent()\n\tsaga.Submit()\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n}\n\nfunc TestSagaGrpcCurrentOrder(t *testing.T) {\n\tsaga := genSagaGrpc(dtmimp.GetFuncName(), false, false).\n\t\tEnableConcurrent().\n\t\tAddBranchOrder(1, []int{0})\n\tsaga.Submit()\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n}\n\nfunc TestSagaGrpcCommittedOngoing(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tsaga := genSagaGrpc(gid, false, false)\n\tbusi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)\n\tsaga.Submit()\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, StatusSubmitted, getTransStatus(saga.Gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusPrepared, StatusPrepared, StatusPrepared}, getBranchesStatus(saga.Gid))\n\tcronTransOnce(t, gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n}\n\nfunc TestSagaGrpcNormalWait(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tsaga := genSagaGrpc(gid, false, false)\n\tsaga.WaitResult = true\n\tsaga.Submit()\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n\twaitTransProcessed(saga.Gid)\n}\n\nfunc TestSagaGrpcEmptyUrl(t *testing.T) {\n\tsaga := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, dtmimp.GetFuncName())\n\treq := busi.GenReqGrpc(30, false, false)\n\tsaga.Add(busi.BusiGrpc+\"/busi.Busi/TransOut\", busi.BusiGrpc+\"/busi.Busi/TransOutRevert\", req)\n\tsaga.Add(\"\", busi.BusiGrpc+\"/busi.Busi/TransInRevert\", req)\n\tsaga.Submit()\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n}\n\n// nolint: unparam\nfunc genSagaGrpc(gid string, outFailed bool, inFailed bool) *dtmgrpc.SagaGrpc {\n\tsaga := dtmgrpc.NewSagaGrpcWithContext(context.Background(), dtmutil.DefaultGrpcServer, gid)\n\treq := busi.GenReqGrpc(30, outFailed, inFailed)\n\tsaga.Add(busi.BusiGrpc+\"/busi.Busi/TransOut\", busi.BusiGrpc+\"/busi.Busi/TransOutRevert\", req)\n\tsaga.Add(busi.BusiGrpc+\"/busi.Busi/TransIn\", busi.BusiGrpc+\"/busi.Busi/TransInRevert\", req)\n\treturn saga\n}\n\nfunc TestSagaGrpcWithGlobalTransRequestTimeout(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tsaga := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, gid)\n\tsaga.WaitResult = true\n\tsaga.Add(busi.BusiGrpc+\"/busi.Busi/TransOutHeaderNo\", \"\", nil)\n\tsaga.WithGlobalTransRequestTimeout(6)\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(gid)\n}\n\nfunc TestSagaGrpcOptionsRollbackWait(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tsaga := genSagaGrpc(gid, false, true)\n\tbusi.MainSwitch.FailureReason.SetOnce(\"Insufficient balance\")\n\tsaga.WaitResult = true\n\terr := saga.Submit()\n\tassert.Error(t, err)\n\tassert.Contains(t, err.Error(), \"Insufficient balance\")\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, StatusFailed, getTransStatus(saga.Gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(saga.Gid))\n\tassert.Contains(t, getTrans(saga.Gid).RollbackReason, \"Insufficient balance\")\n}\n\nfunc TestSagaGrpcHeaders(t *testing.T) {\n\tgidYes := dtmimp.GetFuncName()\n\tsagaYes := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, gidYes).\n\t\tAdd(busi.BusiGrpc+\"/busi.Busi/TransOutHeaderYes\", \"\", nil)\n\tsagaYes.BranchHeaders = map[string]string{\n\t\t\"test_header\": \"test\",\n\t}\n\tsagaYes.WaitResult = true\n\terr := sagaYes.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(gidYes)\n}\n\nfunc TestSagaGrpcCronHeaders(t *testing.T) {\n\tgidYes := dtmimp.GetFuncName()\n\tsagaYes := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, gidYes)\n\tsagaYes.BranchHeaders = map[string]string{\n\t\t\"test_header\": \"test\",\n\t}\n\tsagaYes.Add(busi.BusiGrpc+\"/busi.Busi/TransOutHeaderYes\", \"\", nil)\n\tbusi.MainSwitch.TransOutResult.SetOnce(\"ONGOING\")\n\terr := sagaYes.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(gidYes)\n\tassert.Equal(t, StatusSubmitted, getTransStatus(gidYes))\n\tcronTransOnce(t, gidYes)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gidYes))\n}\n"
  },
  {
    "path": "test/saga_options_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSagaOptionsRetryOngoing(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tsaga := genSaga1(dtmimp.GetFuncName(), false, false)\n\tsaga.RetryInterval = 150 // CronForwardDuration is larger than RetryInterval\n\tbusi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(saga.Gid)\n\tcronTransOnce(t, gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n}\n\nfunc TestSagaOptionsRetryError(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tsaga := genSaga1(dtmimp.GetFuncName(), false, false)\n\tsaga.RetryInterval = 150 // CronForwardDuration is less than 2*RetryInterval\n\tbusi.MainSwitch.TransOutResult.SetOnce(\"ERROR\")\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, StatusSubmitted, getTransStatus(saga.Gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusPrepared}, getBranchesStatus(saga.Gid))\n\tcronTransOnce(t, \"\")\n\tcronTransOnceForwardCron(t, gid, 360)\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n}\n\nfunc TestSagaOptionsTimeout(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tsaga := genSaga(dtmimp.GetFuncName(), false, false)\n\tsaga.TimeoutToFail = 1800\n\tbusi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)\n\tsaga.Submit()\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, StatusSubmitted, getTransStatus(saga.Gid))\n\tcronTransOnceForwardNow(t, gid, 3600)\n\tassert.Equal(t, StatusFailed, getTransStatus(saga.Gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusPrepared, StatusPrepared, StatusPrepared}, getBranchesStatus(saga.Gid))\n\tassert.Regexp(t, `^Timeout after \\d+ seconds$`, getTrans(gid).RollbackReason)\n\n}\n\nfunc TestSagaGlobalTransWithRequestTimeout(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tsaga := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, gid)\n\tsaga.WaitResult = true\n\tsaga.Add(busi.Busi+\"/TransOutTimeout\", \"\", nil)\n\tsaga.WithGlobalTransRequestTimeout(6)\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(gid)\n}\n\nfunc TestSagaOptionsNormalWait(t *testing.T) {\n\tsaga := genSaga(dtmimp.GetFuncName(), false, false)\n\tsaga.WaitResult = true\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n\twaitTransProcessed(saga.Gid)\n}\n\nfunc TestSagaOptionsCommittedOngoingWait(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tsaga := genSaga(dtmimp.GetFuncName(), false, false)\n\tbusi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)\n\tsaga.WaitResult = true\n\terr := saga.Submit()\n\tassert.Error(t, err)\n\tassert.Equal(t, []string{StatusPrepared, StatusPrepared, StatusPrepared, StatusPrepared}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSubmitted, getTransStatus(saga.Gid))\n\twaitTransProcessed(saga.Gid)\n\tcronTransOnce(t, gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n}\n\nfunc TestSagaOptionsRollbackWait(t *testing.T) {\n\tsaga := genSaga(dtmimp.GetFuncName(), false, true)\n\tbusi.MainSwitch.FailureReason.SetOnce(\"Insufficient balance\")\n\tsaga.WaitResult = true\n\terr := saga.Submit()\n\tassert.Error(t, err)\n\tassert.Contains(t, err.Error(), \"Insufficient balance\")\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, StatusFailed, getTransStatus(saga.Gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(saga.Gid))\n\tassert.Contains(t, getTrans(saga.Gid).RollbackReason, \"Insufficient balance\")\n}\n\nfunc TestSagaHeaders(t *testing.T) {\n\tgidYes := dtmimp.GetFuncName()\n\tsagaYes := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, gidYes)\n\tsagaYes.BranchHeaders = map[string]string{\n\t\t\"test_header\": \"test\",\n\t}\n\tsagaYes.WaitResult = true\n\tsagaYes.Add(busi.Busi+\"/TransOutHeaderYes\", \"\", nil)\n\terr := sagaYes.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(gidYes)\n}\n\nfunc TestSagaHeadersYes1(t *testing.T) {\n\tgidYes := dtmimp.GetFuncName()\n\tsagaYes := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, gidYes)\n\tsagaYes.BranchHeaders = map[string]string{\n\t\t\"test_header\": \"test\",\n\t}\n\tsagaYes.Add(busi.Busi+\"/TransOutHeaderYes\", \"\", nil)\n\tbusi.MainSwitch.TransOutResult.SetOnce(\"ONGOING\")\n\terr := sagaYes.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(gidYes)\n\tassert.Equal(t, StatusSubmitted, getTransStatus(gidYes))\n\tcronTransOnce(t, gidYes)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gidYes))\n}\n\nfunc TestSagaGlobalTransWithRetryLimitYes(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tsaga := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, gid)\n\treq := busi.GenReqHTTP(30, false, false)\n\tsaga.Add(busi.Busi+\"/TransOut\", busi.Busi+\"/TransOutRevert\", &req)\n\tsaga.Add(busi.Busi+\"/TransInRetry\", busi.Busi+\"/TransInRevert\", &req)\n\tsaga.WaitResult = true\n\tsaga.WithRetryLimit(3)\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n}\n\nfunc TestSagaGlobalTransWithRetryLimitNo(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tsaga := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, gid)\n\treq := busi.GenReqHTTP(30, false, false)\n\tsaga.Add(busi.Busi+\"/TransOut\", busi.Busi+\"/TransOutRevert\", &req)\n\tsaga.Add(busi.Busi+\"/TransInRetry\", busi.Busi+\"/TransInRevert\", &req)\n\tsaga.WaitResult = true\n\tsaga.WithRetryLimit(1)\n\terr := saga.Submit()\n\tassert.NotNil(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusFailed, getTransStatus(saga.Gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusPrepared}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, `RetryCount is greater than RetryLimit, RetryLimit: 1`, getTrans(gid).RollbackReason)\n}\n"
  },
  {
    "path": "test/saga_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSagaNormal(t *testing.T) {\n\tsaga := genSaga(dtmimp.GetFuncName(), false, false)\n\tsaga.Submit()\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n\tassert.Equal(t, \"\", getTrans(saga.Gid).RollbackReason)\n}\n\nfunc TestSagaRollback(t *testing.T) {\n\tsaga := genSaga(dtmimp.GetFuncName(), false, true)\n\tbusi.MainSwitch.FailureReason.SetOnce(\"Insufficient balance\")\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusFailed, getTransStatus(saga.Gid))\n\tassert.Contains(t, getTrans(saga.Gid).RollbackReason, \"Insufficient balance\")\n}\n\nfunc TestSagaOngoingSucceed(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tsaga := genSaga(dtmimp.GetFuncName(), false, false)\n\tbusi.MainSwitch.TransOutResult.SetOnce(dtmcli.ResultOngoing)\n\tsaga.Submit()\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusPrepared, StatusPrepared, StatusPrepared}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSubmitted, getTransStatus(saga.Gid))\n\tcronTransOnce(t, gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n\tassert.Equal(t, \"\", getTrans(saga.Gid).RollbackReason)\n}\n\nfunc TestSagaFailed(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tsaga := genSaga(dtmimp.GetFuncName(), false, true)\n\tbusi.MainSwitch.TransOutRevertResult.SetOnce(\"ERROR\")\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, StatusAborting, getTransStatus(saga.Gid))\n\tcronTransOnce(t, gid)\n\tassert.Equal(t, StatusFailed, getTransStatus(saga.Gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusSucceed, StatusSucceed, StatusFailed}, getBranchesStatus(saga.Gid))\n}\n\nfunc TestSagaAbnormal(t *testing.T) {\n\tsaga := genSaga(dtmimp.GetFuncName(), false, false)\n\tbusi.MainSwitch.TransOutResult.SetOnce(\"ONGOING\")\n\terr := saga.Submit()\n\tassert.Nil(t, err)\n\twaitTransProcessed(saga.Gid)\n\terr = saga.Submit() // submit twice, ignored\n\tassert.Nil(t, err)\n\twaitTransProcessed(saga.Gid)\n\terr = saga.Submit()\n\tassert.Error(t, err) // a succeed trans can't accept submit\n}\n\nfunc TestSagaEmptyUrl(t *testing.T) {\n\tsaga := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, dtmimp.GetFuncName())\n\treq := busi.GenReqHTTP(30, false, false)\n\tsaga.Add(busi.Busi+\"/TransOut\", \"\", &req)\n\tsaga.Add(\"\", \"\", &req)\n\tsaga.Submit()\n\twaitTransProcessed(saga.Gid)\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))\n\tassert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))\n}\n\nfunc genSaga(gid string, outFailed bool, inFailed bool) *dtmcli.Saga {\n\tsaga := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, gid)\n\treq := busi.GenReqHTTP(30, outFailed, inFailed)\n\tsaga.Add(busi.Busi+\"/TransOut\", busi.Busi+\"/TransOutRevert\", &req)\n\tsaga.Add(busi.Busi+\"/TransIn\", busi.Busi+\"/TransInRevert\", &req)\n\treturn saga\n}\n\nfunc genSaga1(gid string, outFailed bool, inFailed bool) *dtmcli.Saga {\n\tsaga := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, gid)\n\treq := busi.GenReqHTTP(30, outFailed, inFailed)\n\tsaga.Add(busi.Busi+\"/TransOut\", busi.Busi+\"/TransOutRevert\", &req)\n\treturn saga\n}\n"
  },
  {
    "path": "test/store_test.go",
    "content": "package test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage/registry\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc initTransGlobal(gid string) (*storage.TransGlobalStore, storage.Store) {\n\tnext := time.Now().Add(10 * time.Second)\n\treturn initTransGlobalByNextCronTime(gid, next)\n}\n\nfunc initTransGlobalByNextCronTime(gid string, next time.Time) (*storage.TransGlobalStore, storage.Store) {\n\tg := &storage.TransGlobalStore{Gid: gid, Status: \"prepared\", NextCronTime: &next}\n\tbs := []storage.TransBranchStore{\n\t\t{Gid: gid, BranchID: \"01\"},\n\t}\n\ts := registry.GetStore()\n\terr := s.MaySaveNewTrans(g, bs)\n\tdtmimp.E2P(err)\n\treturn g, s\n}\n\nfunc TestStoreSave(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tbs := []storage.TransBranchStore{\n\t\t{Gid: gid, BranchID: \"01\"},\n\t\t{Gid: gid, BranchID: \"02\"},\n\t}\n\tg, s := initTransGlobal(gid)\n\tg2 := s.FindTransGlobalStore(gid)\n\tassert.NotNil(t, g2)\n\tassert.Equal(t, gid, g2.Gid)\n\n\tbs2 := s.FindBranches(gid)\n\tassert.Equal(t, len(bs2), int(1))\n\tassert.Equal(t, \"01\", bs2[0].BranchID)\n\n\ts.LockGlobalSaveBranches(gid, g.Status, []storage.TransBranchStore{bs[1]}, -1)\n\tbs3 := s.FindBranches(gid)\n\tassert.Equal(t, 2, len(bs3))\n\tassert.Equal(t, \"02\", bs3[1].BranchID)\n\tassert.Equal(t, \"01\", bs3[0].BranchID)\n\n\terr := dtmimp.CatchP(func() {\n\t\ts.LockGlobalSaveBranches(g.Gid, \"submitted\", []storage.TransBranchStore{bs[1]}, 1)\n\t})\n\tassert.Equal(t, storage.ErrNotFound, err)\n\n\ts.ChangeGlobalStatus(g, \"succeed\", []string{}, true)\n}\n\nfunc TestStoreChangeStatus(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tg, s := initTransGlobal(gid)\n\tg.Status = \"no\"\n\terr := dtmimp.CatchP(func() {\n\t\ts.ChangeGlobalStatus(g, \"submitted\", []string{}, false)\n\t})\n\tassert.Equal(t, storage.ErrNotFound, err)\n\tg.Status = \"prepared\"\n\ts.ChangeGlobalStatus(g, \"submitted\", []string{}, false)\n\ts.ChangeGlobalStatus(g, \"succeed\", []string{}, true)\n}\n\nfunc TestStoreLockTrans(t *testing.T) {\n\t// lock trans will only lock unfinished trans. ensure all other trans are finished\n\tgid := dtmimp.GetFuncName()\n\tg, s := initTransGlobal(gid)\n\n\tg2 := s.LockOneGlobalTrans(2 * time.Duration(conf.RetryInterval) * time.Second)\n\tassert.NotNil(t, g2)\n\tassert.Equal(t, gid, g2.Gid)\n\n\ts.TouchCronTime(g, 3*conf.RetryInterval, dtmutil.GetNextTime(3*conf.RetryInterval))\n\tg2 = s.LockOneGlobalTrans(2 * time.Duration(conf.RetryInterval) * time.Second)\n\tassert.Nil(t, g2)\n\n\ts.TouchCronTime(g, 1*conf.RetryInterval, dtmutil.GetNextTime(1*conf.RetryInterval))\n\tg2 = s.LockOneGlobalTrans(2 * time.Duration(conf.RetryInterval) * time.Second)\n\tassert.NotNil(t, g2)\n\tassert.Equal(t, gid, g2.Gid)\n\n\ts.ChangeGlobalStatus(g, \"succeed\", []string{}, true)\n\tg2 = s.LockOneGlobalTrans(2 * time.Duration(conf.RetryInterval) * time.Second)\n\tassert.Nil(t, g2)\n}\n\nfunc TestStoreResetCronTime(t *testing.T) {\n\ts := registry.GetStore()\n\ttestStoreResetCronTime(t, dtmimp.GetFuncName(), func(timeout int64, limit int64) (int64, bool, error) {\n\t\treturn s.ResetCronTime(time.Duration(timeout)*time.Second, limit)\n\t})\n}\n\nfunc testStoreResetCronTime(t *testing.T, funcName string, resetCronHandler func(expire int64, limit int64) (int64, bool, error)) {\n\ts := registry.GetStore()\n\tvar afterSeconds, lockExpireIn, limit, i int64\n\tafterSeconds = 100\n\tlockExpireIn = 2\n\tlimit = 10\n\n\t// Will be reset\n\tfor i = 0; i < limit; i++ {\n\t\tgid := funcName + fmt.Sprintf(\"%d\", i)\n\t\t_, _ = initTransGlobalByNextCronTime(gid, time.Now().Add(time.Duration(afterSeconds+10)*time.Second))\n\t}\n\n\t// Will not be reset\n\tgid := funcName + fmt.Sprintf(\"%d\", 10)\n\t_, _ = initTransGlobalByNextCronTime(gid, time.Now().Add(time.Duration(afterSeconds-10)*time.Second))\n\n\t// Not Found\n\tg := s.LockOneGlobalTrans(time.Duration(lockExpireIn) * time.Second)\n\tassert.Nil(t, g)\n\n\t// Reset limit-1 count\n\tsucceedCount, hasRemaining, err := resetCronHandler(afterSeconds, limit-1)\n\tassert.Equal(t, hasRemaining, true)\n\tassert.Equal(t, succeedCount, limit-1)\n\tassert.Nil(t, err)\n\t// Found limit-1 count\n\tfor i = 0; i < limit-1; i++ {\n\t\tg = s.LockOneGlobalTrans(time.Duration(lockExpireIn) * time.Second)\n\t\tassert.NotNil(t, g)\n\t\ts.ChangeGlobalStatus(g, \"succeed\", []string{}, true)\n\t}\n\n\t// Not Found\n\tg = s.LockOneGlobalTrans(time.Duration(lockExpireIn) * time.Second)\n\tassert.Nil(t, g)\n\n\t// Reset 1 count\n\tsucceedCount, hasRemaining, err = resetCronHandler(afterSeconds, limit)\n\tassert.Equal(t, hasRemaining, false)\n\tassert.Equal(t, succeedCount, int64(1))\n\tassert.Nil(t, err)\n\t// Found 1 count\n\tg = s.LockOneGlobalTrans(time.Duration(lockExpireIn) * time.Second)\n\tassert.NotNil(t, g)\n\ts.ChangeGlobalStatus(g, \"succeed\", []string{}, true)\n\n\t// Not Found\n\tg = s.LockOneGlobalTrans(time.Duration(lockExpireIn) * time.Second)\n\tassert.Nil(t, g)\n\n\t// reduce the resetTimeTimeout, Reset 1 count\n\tsucceedCount, hasRemaining, err = resetCronHandler(afterSeconds-12, limit)\n\tassert.Equal(t, hasRemaining, false)\n\tassert.Equal(t, succeedCount, int64(1))\n\tassert.Nil(t, err)\n\t// Found 1 count\n\tg = s.LockOneGlobalTrans(time.Duration(lockExpireIn) * time.Second)\n\tassert.NotNil(t, g)\n\ts.ChangeGlobalStatus(g, \"succeed\", []string{}, true)\n\n\t// Not Found\n\tg = s.LockOneGlobalTrans(time.Duration(lockExpireIn) * time.Second)\n\tassert.Nil(t, g)\n\n\t// Not Found\n\tsucceedCount, hasRemaining, err = resetCronHandler(afterSeconds-12, limit)\n\tassert.Equal(t, hasRemaining, false)\n\tassert.Equal(t, succeedCount, int64(0))\n\tassert.Nil(t, err)\n}\n\nfunc TestUpdateBranches(t *testing.T) {\n\tif !conf.Store.IsDB() {\n\t\t_, err := registry.GetStore().UpdateBranches(nil, nil)\n\t\tassert.Nil(t, err)\n\t}\n}\n\nfunc TestResetTransGlobalCronTime(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tg, _ := initTransGlobal(gid)\n\n\ts := registry.GetStore()\n\tg2 := s.FindTransGlobalStore(gid)\n\tassert.NotNil(t, g2)\n\tassert.Equal(t, gid, g2.Gid)\n\n\ts.ResetTransGlobalCronTime(g2)\n\n\tg2 = s.FindTransGlobalStore(gid)\n\tassert.NotNil(t, g2)\n\tassert.Equal(t, gid, g2.Gid)\n\tassert.Greater(t, time.Now().Add(3*time.Second), *g2.NextCronTime)\n\tassert.Equal(t, g2.UpdateTime, g2.NextCronTime)\n\tassert.NotEqual(t, g.UpdateTime, g2.UpdateTime)\n\tassert.NotEqual(t, g.NextCronTime, g2.NextCronTime)\n\ts.ChangeGlobalStatus(g, \"succeed\", []string{}, true)\n}\n"
  },
  {
    "path": "test/tcc_barrier_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/go-resty/resty/v2\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestTccBarrierNormal(t *testing.T) {\n\treq := busi.GenReqHTTP(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\terr := dtmcli.TccGlobalTransaction(DtmServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {\n\t\t_, err := tcc.CallBranch(req, Busi+\"/TccBTransOutTry\", Busi+\"/TccBTransOutConfirm\", Busi+\"/TccBTransOutCancel\")\n\t\tassert.Nil(t, err)\n\t\treturn tcc.CallBranch(req, Busi+\"/TccBTransInTry\", Busi+\"/TccBTransInConfirm\", Busi+\"/TccBTransInCancel\")\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid))\n}\n\nfunc TestTccBarrierRollback(t *testing.T) {\n\treq := busi.GenReqHTTP(30, false, true)\n\tgid := dtmimp.GetFuncName()\n\terr := dtmcli.TccGlobalTransaction(DtmServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {\n\t\t_, err := tcc.CallBranch(req, Busi+\"/TccBTransOutTry\", Busi+\"/TccBTransOutConfirm\", Busi+\"/TccBTransOutCancel\")\n\t\tassert.Nil(t, err)\n\t\treturn tcc.CallBranch(req, Busi+\"/TccBTransInTry\", Busi+\"/TccBTransInConfirm\", Busi+\"/TccBTransInCancel\")\n\t})\n\tassert.Error(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusFailed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusPrepared, StatusSucceed, StatusPrepared}, getBranchesStatus(gid))\n}\n\nfunc TestTccBarrierDisorderMysql(t *testing.T) {\n\trunTestTccBarrierDisorder(t, \"mysql\")\n}\n\nfunc TestTccBarrierDisorderMongo(t *testing.T) {\n\trunTestTccBarrierDisorder(t, \"mongo\")\n}\n\nfunc TestTccBarrierDisorderRedis(t *testing.T) {\n\tbusi.SetRedisBothAccount(200, 200)\n\trunTestTccBarrierDisorder(t, \"redis\")\n}\n\nfunc runTestTccBarrierDisorder(t *testing.T, store string) {\n\tif store == \"mongo\" {\n\t\tMaySkipMongo(t)\n\t}\n\tbefore := getBeforeBalances(store)\n\tcancelFinishedChan := make(chan string, 2)\n\tcancelCanReturnChan := make(chan string, 2)\n\tgid := dtmimp.GetFuncName() + store\n\tcronFinished := make(chan string, 2)\n\terr := dtmcli.TccGlobalTransaction(DtmServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {\n\t\tbody := &busi.ReqHTTP{Amount: 30, Store: store}\n\t\ttryURL := Busi + \"/TccBTransOutTry\"\n\t\tconfirmURL := Busi + \"/TccBTransOutConfirm\"\n\t\tcancelURL := Busi + \"/SleepCancel\"\n\t\t// refer to time diagram for barrier, here we simulate it\n\t\tbranchID := tcc.NewSubBranchID()\n\t\tbusi.SetSleepCancelHandler(func(c *gin.Context) interface{} {\n\t\t\tres := busi.TccBarrierTransOutCancel(c)\n\t\t\tlogger.Debugf(\"disorderHandler before cancel finish write\")\n\t\t\tcancelFinishedChan <- \"1\"\n\t\t\tlogger.Debugf(\"disorderHandler before cancel return read\")\n\t\t\t<-cancelCanReturnChan\n\t\t\tlogger.Debugf(\"disorderHandler after cancel return read\")\n\t\t\treturn res\n\t\t})\n\t\t// register tcc branch\n\t\tresp, err := dtmcli.GetRestyClient().R().\n\t\t\tSetBody(map[string]interface{}{\n\t\t\t\t\"gid\":            tcc.Gid,\n\t\t\t\t\"branch_id\":      branchID,\n\t\t\t\t\"trans_type\":     \"tcc\",\n\t\t\t\t\"status\":         StatusPrepared,\n\t\t\t\t\"data\":           string(dtmimp.MustMarshal(body)),\n\t\t\t\tdtmimp.OpConfirm: confirmURL,\n\t\t\t\tdtmimp.OpCancel:  cancelURL,\n\t\t\t}).Post(fmt.Sprintf(\"%s/%s\", tcc.Dtm, \"registerBranch\"))\n\t\tassert.Nil(t, err)\n\t\tassert.Contains(t, resp.String(), dtmcli.ResultSuccess)\n\n\t\tlogger.Debugf(\"cron to timeout and then call cancelled twice\")\n\t\tcron := func() {\n\t\t\tcronTransOnceForwardNow(t, gid, 300)\n\t\t\tlogger.Debugf(\"cronFinished write\")\n\t\t\tcronFinished <- \"1\"\n\t\t\tlogger.Debugf(\"cronFinished after write\")\n\t\t}\n\t\tgo cron()\n\t\t<-cancelFinishedChan\n\t\tgo cron()\n\t\t<-cancelFinishedChan\n\t\tcancelCanReturnChan <- \"1\"\n\t\tcancelCanReturnChan <- \"1\"\n\t\tlogger.Debugf(\"after cancelCanRetrun 2 write\")\n\t\t// after cancel then run try\n\t\tr, _ := dtmcli.GetRestyClient().R().\n\t\t\tSetBody(body).\n\t\t\tSetQueryParams(map[string]string{\n\t\t\t\t\"dtm\":        tcc.Dtm,\n\t\t\t\t\"gid\":        tcc.Gid,\n\t\t\t\t\"branch_id\":  branchID,\n\t\t\t\t\"trans_type\": \"tcc\",\n\t\t\t\t\"op\":         dtmimp.OpTry,\n\t\t\t}).\n\t\t\tPost(tryURL)\n\t\tassert.Equal(t, r.StatusCode(), 200) // dangle op, return success\n\t\tlogger.Debugf(\"cronFinished read\")\n\t\t<-cronFinished\n\t\t<-cronFinished\n\t\tlogger.Debugf(\"cronFinished after read\")\n\n\t\treturn nil, fmt.Errorf(\"a cancelled tcc\")\n\t})\n\tassert.Error(t, err, fmt.Errorf(\"a cancelled tcc\"))\n\tassert.Equal(t, []string{StatusSucceed, StatusPrepared}, getBranchesStatus(gid))\n\tassert.Equal(t, StatusFailed, getTransStatus(gid))\n\tassertSameBalance(t, before, store)\n}\n\nfunc TestTccBarrierPanic(t *testing.T) {\n\tbb := &dtmcli.BranchBarrier{TransType: \"saga\", Gid: \"gid1\", BranchID: \"bid1\", Op: \"action\", BarrierID: 1}\n\tvar err error\n\tfunc() {\n\t\tdefer dtmimp.P2E(&err)\n\t\ttx, _ := dbGet().ToSQLDB().BeginTx(context.Background(), &sql.TxOptions{})\n\t\tbb.Call(tx, func(tx *sql.Tx) error {\n\t\t\tpanic(fmt.Errorf(\"an error\"))\n\t\t})\n\t}()\n\tassert.Error(t, err, fmt.Errorf(\"an error\"))\n}\n"
  },
  {
    "path": "test/tcc_cover_test.go",
    "content": "package test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/go-resty/resty/v2\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestTccCoverNotConnected(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\terr := dtmcli.TccGlobalTransaction(\"localhost:01\", gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {\n\t\treturn nil, nil\n\t})\n\tassert.Error(t, err)\n}\n\nfunc TestTccCoverPanic(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\terr := dtmimp.CatchP(func() {\n\t\t_ = dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {\n\t\t\tpanic(\"user panic\")\n\t\t})\n\t\tassert.FailNow(t, \"not executed\")\n\t})\n\tassert.Contains(t, err.Error(), \"user panic\")\n\twaitTransProcessed(gid)\n}\n\nfunc TestTccNested(t *testing.T) {\n\treq := busi.GenReqHTTP(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\terr := dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {\n\t\t_, err := tcc.CallBranch(req, Busi+\"/TransOut\", Busi+\"/TransOutConfirm\", Busi+\"/TransOutRevert\")\n\t\tassert.Nil(t, err)\n\t\treturn tcc.CallBranch(req, Busi+\"/TransInTccNested\", Busi+\"/TransInConfirm\", Busi+\"/TransInRevert\")\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid))\n}\n"
  },
  {
    "path": "test/tcc_grpc_cover_test.go",
    "content": "package test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"google.golang.org/protobuf/types/known/emptypb\"\n)\n\nfunc TestTccGrpcCoverNotConnected(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\terr := dtmgrpc.TccGlobalTransaction(\"localhost:01\", gid, func(tcc *dtmgrpc.TccGrpc) error {\n\t\treturn nil\n\t})\n\tassert.Error(t, err)\n}\n\nfunc TestTccGrpcCoverPanic(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\terr := dtmimp.CatchP(func() {\n\t\t_ = dtmgrpc.TccGlobalTransaction(dtmutil.DefaultGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {\n\t\t\tpanic(\"user panic\")\n\t\t})\n\t\tassert.FailNow(t, \"not executed\")\n\t})\n\tassert.Contains(t, err.Error(), \"user panic\")\n\twaitTransProcessed(gid)\n}\n\nfunc TestTccGrpcCoverCallBranch(t *testing.T) {\n\treq := busi.GenReqGrpc(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\terr := dtmgrpc.TccGlobalTransaction(dtmutil.DefaultGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {\n\n\t\tr := &emptypb.Empty{}\n\t\terr := tcc.CallBranch(req, \"not_exists://abc\", busi.BusiGrpc+\"/busi.Busi/TransOutConfirm\", busi.BusiGrpc+\"/busi.Busi/TransOutRevert\", r)\n\t\tassert.Error(t, err)\n\n\t\ttcc.Dtm = \"localhost:01\"\n\t\terr = tcc.CallBranch(req, busi.BusiGrpc+\"/busi.Busi/TransOut\", busi.BusiGrpc+\"/busi.Busi/TransOutConfirm\", busi.BusiGrpc+\"/busi.Busi/TransOutRevert\", r)\n\t\tassert.Error(t, err)\n\n\t\treturn err\n\t})\n\tassert.Error(t, err)\n\tcronTransOnceForwardNow(t, gid, 300)\n}\n"
  },
  {
    "path": "test/tcc_grpc_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"google.golang.org/protobuf/types/known/emptypb\"\n)\n\nfunc TestTccGrpcNormal(t *testing.T) {\n\treq := busi.GenReqGrpc(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\terr := dtmgrpc.TccGlobalTransaction(dtmutil.DefaultGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {\n\t\tr := &emptypb.Empty{}\n\t\terr := tcc.CallBranch(req, busi.BusiGrpc+\"/busi.Busi/TransOut\", busi.BusiGrpc+\"/busi.Busi/TransOutConfirm\", busi.BusiGrpc+\"/busi.Busi/TransOutRevert\", r)\n\t\tassert.Nil(t, err)\n\t\treturn tcc.CallBranch(req, busi.BusiGrpc+\"/busi.Busi/TransIn\", busi.BusiGrpc+\"/busi.Busi/TransInConfirm\", busi.BusiGrpc+\"/busi.Busi/TransInRevert\", r)\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid))\n\n}\n\nfunc TestTccGrpcRollback(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqGrpc(30, false, true)\n\terr := dtmgrpc.TccGlobalTransaction(dtmutil.DefaultGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {\n\t\tr := &emptypb.Empty{}\n\t\terr := tcc.CallBranch(req, busi.BusiGrpc+\"/busi.Busi/TransOutTcc\", busi.BusiGrpc+\"/busi.Busi/TransOutConfirm\", busi.BusiGrpc+\"/busi.Busi/TransOutRevert\", r)\n\t\tassert.Nil(t, err)\n\t\tbusi.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)\n\t\treturn tcc.CallBranch(req, busi.BusiGrpc+\"/busi.Busi/TransInTcc\", busi.BusiGrpc+\"/busi.Busi/TransInConfirm\", busi.BusiGrpc+\"/busi.Busi/TransInRevert\", r)\n\t})\n\tassert.Error(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusAborting, getTransStatus(gid))\n\tcronTransOnce(t, gid)\n\tassert.Equal(t, StatusFailed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusPrepared, StatusSucceed, StatusPrepared}, getBranchesStatus(gid))\n\tassert.Equal(t, \"rpc error: code = Aborted desc = reason:\", getTrans(gid).RollbackReason)\n}\n\nfunc TestTccGrpcNested(t *testing.T) {\n\treq := busi.GenReqGrpc(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\terr := dtmgrpc.TccGlobalTransaction(dtmutil.DefaultGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {\n\t\tr := &emptypb.Empty{}\n\t\terr := tcc.CallBranch(req, busi.BusiGrpc+\"/busi.Busi/TransOutTcc\", busi.BusiGrpc+\"/busi.Busi/TransOutConfirm\", busi.BusiGrpc+\"/busi.Busi/TransOutRevert\", r)\n\t\tassert.Nil(t, err)\n\t\treturn tcc.CallBranch(req, busi.BusiGrpc+\"/busi.Busi/TransInTccNested\", busi.BusiGrpc+\"/busi.Busi/TransInConfirm\", busi.BusiGrpc+\"/busi.Busi/TransInRevert\", r)\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid))\n}\n\nfunc TestTccGrpcType(t *testing.T) {\n\t_, err := dtmgrpc.TccFromGrpc(context.Background())\n\tassert.Error(t, err)\n\tlogger.Debugf(\"expecting dtmutil.DefaultGrpcServer error\")\n\terr = dtmgrpc.TccGlobalTransaction(\"-\", \"\", func(tcc *dtmgrpc.TccGrpc) error { return nil })\n\tassert.Error(t, err)\n}\n\nfunc TestTccGrpcHeaders(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\terr := dtmgrpc.TccGlobalTransaction2(dtmutil.DefaultGrpcServer, gid, func(tg *dtmgrpc.TccGrpc) {\n\t\ttg.BranchHeaders = map[string]string{\n\t\t\t\"test_header\": \"test\",\n\t\t}\n\t\ttg.WaitResult = true\n\t}, func(tcc *dtmgrpc.TccGrpc) error {\n\t\tdata := &busi.ReqGrpc{Amount: 30}\n\t\tr := &emptypb.Empty{}\n\t\treturn tcc.CallBranch(data, busi.BusiGrpc+\"/busi.Busi/TransOutHeaderYes\", \"\", \"\", r)\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed}, getBranchesStatus(gid))\n\n}\n"
  },
  {
    "path": "test/tcc_jrpc_test.go",
    "content": "package test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/go-resty/resty/v2\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestTccJrpcNormal(t *testing.T) {\n\treq := busi.GenReqHTTP(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\terr := dtmcli.TccGlobalTransaction2(dtmutil.DefaultJrpcServer, gid, func(tcc *dtmcli.Tcc) {\n\t\ttcc.Protocol = dtmimp.Jrpc\n\t}, func(tcc *dtmcli.Tcc) (*resty.Response, error) {\n\t\t_, err := tcc.CallBranch(req, Busi+\"/TransOut\", Busi+\"/TransOutConfirm\", Busi+\"/TransOutRevert\")\n\t\tassert.Nil(t, err)\n\t\treturn tcc.CallBranch(req, Busi+\"/TransIn\", Busi+\"/TransInConfirm\", Busi+\"/TransInRevert\")\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid))\n}\n"
  },
  {
    "path": "test/tcc_old_test.go",
    "content": "package test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/go-resty/resty/v2\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestTccOldNormal(t *testing.T) {\n\treq := busi.GenReqHTTP(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\terr := dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {\n\t\t_, err := tcc.CallBranch(req, Busi+\"/TransOutOld\", Busi+\"/TransOutConfirmOld\", Busi+\"/TransOutRevertOld\")\n\t\tassert.Nil(t, err)\n\t\treturn tcc.CallBranch(req, Busi+\"/TransInOld\", Busi+\"/TransInConfirmOld\", Busi+\"/TransInRevertOld\")\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid))\n}\n\nfunc TestTccOldRollback(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, true)\n\terr := dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {\n\t\t_, rerr := tcc.CallBranch(req, Busi+\"/TransOutOld\", Busi+\"/TransOutConfirmOld\", Busi+\"/TransOutRevertOld\")\n\t\tassert.Nil(t, rerr)\n\t\tbusi.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)\n\t\treturn tcc.CallBranch(req, Busi+\"/TransInOld\", Busi+\"/TransInConfirmOld\", Busi+\"/TransInRevertOld\")\n\t})\n\tassert.Error(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusAborting, getTransStatus(gid))\n\tcronTransOnce(t, gid)\n\tassert.Equal(t, StatusFailed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusPrepared, StatusSucceed, StatusPrepared}, getBranchesStatus(gid))\n}\n\nfunc TestTccOldTimeout(t *testing.T) {\n\treq := busi.GenReqHTTP(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\ttimeoutChan := make(chan int, 1)\n\n\terr := dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {\n\t\t_, err := tcc.CallBranch(req, Busi+\"/TransOutOld\", Busi+\"/TransOutConfirmOld\", Busi+\"/TransOutRevertOld\")\n\t\tassert.Nil(t, err)\n\t\tgo func() {\n\t\t\tcronTransOnceForwardNow(t, gid, 300)\n\t\t\ttimeoutChan <- 0\n\t\t}()\n\t\t<-timeoutChan\n\t\t_, err = tcc.CallBranch(req, Busi+\"/TransInOld\", Busi+\"/TransInConfirmOld\", Busi+\"/TransInRevertOld\")\n\t\tassert.Error(t, err)\n\t\treturn nil, err\n\t})\n\tassert.Error(t, err)\n\tassert.Equal(t, StatusFailed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusPrepared}, getBranchesStatus(gid))\n}\n"
  },
  {
    "path": "test/tcc_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/go-resty/resty/v2\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestTccNormal(t *testing.T) {\n\treq := busi.GenReqHTTP(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\terr := dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {\n\t\t_, err := tcc.CallBranch(req, Busi+\"/TransOut\", Busi+\"/TransOutConfirm\", Busi+\"/TransOutRevert\")\n\t\tassert.Nil(t, err)\n\t\treturn tcc.CallBranch(req, Busi+\"/TransIn\", Busi+\"/TransInConfirm\", Busi+\"/TransInRevert\")\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid))\n}\n\nfunc TestTccRollback(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\treq := busi.GenReqHTTP(30, false, true)\n\terr := dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {\n\t\t_, rerr := tcc.CallBranch(req, Busi+\"/TransOut\", Busi+\"/TransOutConfirm\", Busi+\"/TransOutRevert\")\n\t\tassert.Nil(t, rerr)\n\t\tbusi.MainSwitch.TransOutRevertResult.SetOnce(dtmcli.ResultOngoing)\n\t\treturn tcc.CallBranch(req, Busi+\"/TransIn\", Busi+\"/TransInConfirm\", Busi+\"/TransInRevert\")\n\t})\n\tassert.Error(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusAborting, getTransStatus(gid))\n\tcronTransOnce(t, gid)\n\tassert.Equal(t, StatusFailed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusPrepared, StatusSucceed, StatusPrepared}, getBranchesStatus(gid))\n\tassert.Contains(t, getTrans(gid).RollbackReason, dtmcli.ResultFailure)\n}\n\nfunc TestTccTimeout(t *testing.T) {\n\treq := busi.GenReqHTTP(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\ttimeoutChan := make(chan int, 1)\n\n\terr := dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {\n\t\t_, err := tcc.CallBranch(req, Busi+\"/TransOut\", Busi+\"/TransOutConfirm\", Busi+\"/TransOutRevert\")\n\t\tassert.Nil(t, err)\n\t\tgo func() {\n\t\t\tcronTransOnceForwardNow(t, gid, 300)\n\t\t\ttimeoutChan <- 0\n\t\t}()\n\t\t<-timeoutChan\n\t\t_, err = tcc.CallBranch(req, Busi+\"/TransIn\", Busi+\"/TransInConfirm\", Busi+\"/TransInRevert\")\n\t\tassert.Error(t, err)\n\t\treturn nil, err\n\t})\n\tassert.Error(t, err)\n\tassert.Equal(t, StatusFailed, getTransStatus(gid))\n\tassert.Regexp(t, `^Timeout after \\d+ seconds$`, getTrans(gid).RollbackReason)\n\tassert.Equal(t, []string{StatusSucceed, StatusPrepared}, getBranchesStatus(gid))\n}\n\nfunc TestTccCompatible(t *testing.T) {\n\treq := busi.GenReqHTTP(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\terr := dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {\n\t\t_, err := tcc.CallBranch(req, Busi+\"/TransOut\", Busi+\"/TransOutConfirm\", Busi+\"/TransOutRevert\")\n\t\tassert.Nil(t, err)\n\t\treturn tcc.CallBranch(req, Busi+\"/TransIn\", Busi+\"/TransInConfirm\", Busi+\"/TransInRevert\")\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid))\n\n}\n\nfunc TestTccHeaders(t *testing.T) {\n\treq := busi.GenReqHTTP(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\terr := dtmcli.TccGlobalTransaction2(dtmutil.DefaultHTTPServer, gid, func(t *dtmcli.Tcc) {\n\t\tt.BranchHeaders = map[string]string{\n\t\t\t\"test_header\": \"test\",\n\t\t}\n\t}, func(tcc *dtmcli.Tcc) (*resty.Response, error) {\n\t\treturn tcc.CallBranch(req, Busi+\"/TransOutHeaderYes\", \"\", \"\")\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed}, getBranchesStatus(gid))\n}\n"
  },
  {
    "path": "test/topic_test.go",
    "content": "package test\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgpb\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nconst (\n\ttestTopicTestTopicNormal           = \"test_topic_TestTopicNormal\"\n\ttestTopicTestConcurrentUpdateTopic = \"concurrent_topic_TestConcurrentUpdateTopic\"\n)\n\nfunc TestTopicNormal(t *testing.T) {\n\ttestSubscribe(t, httpSubscribe)\n\ttestUnsubscribe(t, httpUnsubscribe)\n\ttestDeleteTopic(t, httpDeleteTopic)\n\n\ttestSubscribe(t, grpcSubscribe)\n\ttestUnsubscribe(t, grpcUnsubscribe)\n\ttestDeleteTopic(t, grpcDeleteTopic)\n}\n\nfunc TestConcurrentUpdateTopic(t *testing.T) {\n\tvar wg sync.WaitGroup\n\tvar urls []string\n\tvar errNum int\n\tconcurrentTimes := 20\n\t// concurrently updates the topic, part of them succeed\n\tfor i := 0; i < concurrentTimes; i++ {\n\t\twg.Add(1)\n\t\tgo func(i int) {\n\t\t\turl := \"http://dtm/test\" + strconv.Itoa(i)\n\t\t\terr := httpSubscribe(testTopicTestConcurrentUpdateTopic, url)\n\t\t\tif err == nil {\n\t\t\t\turls = append(urls, url)\n\t\t\t} else {\n\t\t\t\terrNum++\n\t\t\t}\n\t\t\twg.Done()\n\t\t}(i)\n\t}\n\twg.Wait()\n\tassert.True(t, len(urls) > 0)\n\n\t// delete successfully subscribed urls above, all of them should succeed\n\tfor _, url := range urls {\n\t\tassert.Nil(t, httpUnsubscribe(testTopicTestConcurrentUpdateTopic, url))\n\t}\n\n\t// finally, the topic version should be correct\n\tm := map[string]interface{}{}\n\tresp, err := dtmcli.GetRestyClient().R().SetQueryParams(map[string]string{\n\t\t\"cat\": \"topics\",\n\t\t\"key\": testTopicTestConcurrentUpdateTopic,\n\t}).Get(dtmutil.DefaultHTTPServer + \"/queryKV\")\n\tassert.Nil(t, err)\n\tdtmimp.MustUnmarshalString(resp.String(), &m)\n\tdtmimp.MustRemarshal(m[\"kv\"].([]interface{})[0], &m)\n\tassert.Equal(t, float64((concurrentTimes-errNum)*2), m[\"version\"])\n}\n\nfunc testSubscribe(t *testing.T, subscribe func(topic, url string) error) {\n\tassert.Nil(t, subscribe(testTopicTestTopicNormal, \"http://dtm/test1\"))\n\tassert.Error(t, subscribe(testTopicTestTopicNormal, \"http://dtm/test1\")) // error:repeat subscription\n\tassert.Error(t, subscribe(\"\", \"http://dtm/test1\"))                       // error:empty topic\n\tassert.Error(t, subscribe(testTopicTestTopicNormal, \"\"))                 // error:empty url\n\tassert.Nil(t, subscribe(testTopicTestTopicNormal, \"http://dtm/test2\"))\n}\n\nfunc testUnsubscribe(t *testing.T, unsubscribe func(topic, url string) error) {\n\tassert.Nil(t, unsubscribe(testTopicTestTopicNormal, \"http://dtm/test1\"))\n\tassert.Error(t, unsubscribe(testTopicTestTopicNormal, \"http://dtm/test1\")) // error:repeat unsubscription\n\tassert.Error(t, unsubscribe(\"\", \"http://dtm/test1\"))                       // error:empty topic\n\tassert.Error(t, unsubscribe(testTopicTestTopicNormal, \"\"))                 // error:empty url\n\tassert.Error(t, unsubscribe(\"non_existent_topic\", \"http://dtm/test1\"))     // error:unsubscribe a non-existent topic\n\tassert.Nil(t, unsubscribe(testTopicTestTopicNormal, \"http://dtm/test2\"))\n\tassert.Error(t, unsubscribe(testTopicTestTopicNormal, \"http://dtm/test2\"))\n}\n\nfunc testDeleteTopic(t *testing.T, deleteTopic func(topic string) error) {\n\tassert.Error(t, deleteTopic(\"non_existent_testDeleteTopic\"))\n\tassert.Nil(t, deleteTopic(testTopicTestTopicNormal))\n}\n\nfunc httpSubscribe(topic, url string) error {\n\tresp, err := dtmcli.GetRestyClient().R().SetQueryParams(map[string]string{\n\t\t\"topic\":  topic,\n\t\t\"url\":    url,\n\t\t\"remark\": \"for test\",\n\t}).Get(dtmutil.DefaultHTTPServer + \"/subscribe\")\n\te2p(err)\n\tif resp.StatusCode() != 200 {\n\t\terr = errors.Errorf(\"Http Request Error. Resp:%v\", resp.String())\n\t}\n\treturn err\n}\n\nfunc httpUnsubscribe(topic, url string) error {\n\tresp, err := dtmcli.GetRestyClient().R().SetQueryParams(map[string]string{\n\t\t\"topic\": topic,\n\t\t\"url\":   url,\n\t}).Get(dtmutil.DefaultHTTPServer + \"/unsubscribe\")\n\te2p(err)\n\tif resp.StatusCode() != 200 {\n\t\terr = errors.Errorf(\"Http Request Error. Resp:%+v\", resp.String())\n\t}\n\treturn err\n}\n\nfunc httpDeleteTopic(topic string) error {\n\tresp, err := dtmcli.GetRestyClient().R().Delete(dtmutil.DefaultHTTPServer + \"/topic/\" + topic)\n\te2p(err)\n\tif resp.StatusCode() != 200 {\n\t\terr = errors.Errorf(\"Http Request Error. Resp:%+v\", resp.String())\n\t}\n\treturn err\n}\n\nfunc grpcSubscribe(topic, url string) error {\n\t_, err := dtmgimp.MustGetDtmClient(dtmutil.DefaultGrpcServer).Subscribe(context.Background(),\n\t\t&dtmgpb.DtmTopicRequest{\n\t\t\tTopic:  topic,\n\t\t\tURL:    url,\n\t\t\tRemark: \"for test\"})\n\treturn err\n}\n\nfunc grpcUnsubscribe(topic, url string) error {\n\t_, err := dtmgimp.MustGetDtmClient(dtmutil.DefaultGrpcServer).Unsubscribe(context.Background(),\n\t\t&dtmgpb.DtmTopicRequest{\n\t\t\tTopic: topic,\n\t\t\tURL:   url})\n\treturn err\n}\n\nfunc grpcDeleteTopic(topic string) error {\n\t_, err := dtmgimp.MustGetDtmClient(dtmutil.DefaultGrpcServer).DeleteTopic(context.Background(),\n\t\t&dtmgpb.DtmTopicRequest{\n\t\t\tTopic: topic})\n\treturn err\n}\n"
  },
  {
    "path": "test/types.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmsvr\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/config\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar conf = &config.Config\n\nfunc dbGet() *dtmutil.DB {\n\treturn dtmutil.DbGet(busi.BusiConf)\n}\n\n// waitTransProcessed only for test usage. wait for transaction processed once\nfunc waitTransProcessed(gid string) {\n\tlogger.Debugf(\"waiting for gid %s\", gid)\n\tselect {\n\tcase id := <-dtmsvr.TransProcessedTestChan:\n\t\tlogger.FatalfIf(id != gid, \"------- expecting: %s but %s found\", gid, id)\n\t\tlogger.Debugf(\"finish for gid %s\", gid)\n\tcase <-time.After(time.Duration(time.Second * 40000)):\n\t\tlogger.FatalfIf(true, \"Wait Trans timeout\")\n\t}\n}\n\nfunc cronTransOnce(t *testing.T, gid string) {\n\tgid2 := dtmsvr.CronTransOnce()\n\tassert.Equal(t, gid, gid2)\n\tif dtmsvr.TransProcessedTestChan != nil && gid != \"\" {\n\t\twaitTransProcessed(gid)\n\t}\n}\n\nvar e2p = dtmimp.E2P\n\n// TransGlobal alias\ntype TransGlobal = dtmsvr.TransGlobal\n\n// TransBranch alias\ntype TransBranch = dtmsvr.TransBranch\n\nfunc cronTransOnceForwardNow(t *testing.T, gid string, seconds int) {\n\told := dtmsvr.NowForwardDuration\n\tdtmsvr.NowForwardDuration = time.Duration(seconds) * time.Second\n\tcronTransOnce(t, gid)\n\tdtmsvr.NowForwardDuration = old\n}\n\nfunc cronTransOnceForwardCron(t *testing.T, gid string, seconds int) {\n\told := dtmsvr.CronForwardDuration\n\tdtmsvr.CronForwardDuration = time.Duration(seconds) * time.Second\n\tcronTransOnce(t, gid)\n\tdtmsvr.CronForwardDuration = old\n}\n\nfunc submitForwardCron(seconds int, fn func()) {\n\told := dtmsvr.CronForwardDuration\n\tdtmsvr.CronForwardDuration = time.Duration(seconds) * time.Second\n\tfn()\n\tdtmsvr.CronForwardDuration = old\n}\n\nconst (\n\t// StatusPrepared status for global/branch trans status.\n\tStatusPrepared = dtmcli.StatusPrepared\n\t// StatusSubmitted status for global trans status.\n\tStatusSubmitted = dtmcli.StatusSubmitted\n\t// StatusSucceed status for global/branch trans status.\n\tStatusSucceed = dtmcli.StatusSucceed\n\t// StatusFailed status for global/branch trans status.\n\tStatusFailed = dtmcli.StatusFailed\n\t// StatusAborting status for global trans status.\n\tStatusAborting = dtmcli.StatusAborting\n)\n\nfunc getBeforeBalances(store string) []int {\n\tb1 := busi.GetBalanceByUID(busi.TransOutUID, store)\n\tb2 := busi.GetBalanceByUID(busi.TransInUID, store)\n\treturn []int{b1, b2}\n}\n\nfunc assertSameBalance(t *testing.T, before []int, store string) {\n\tb1 := busi.GetBalanceByUID(busi.TransOutUID, store)\n\tb2 := busi.GetBalanceByUID(busi.TransInUID, store)\n\tassert.Equal(t, before[0], b1)\n\tassert.Equal(t, before[1], b2)\n}\n\nfunc assertNotSameBalance(t *testing.T, before []int, store string) {\n\tb1 := busi.GetBalanceByUID(busi.TransOutUID, store)\n\tb2 := busi.GetBalanceByUID(busi.TransInUID, store)\n\tassert.NotEqual(t, before[0], b1)\n\tassert.Equal(t, before[0]+before[1], b1+b2)\n}\n"
  },
  {
    "path": "test/workflow_base_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmsvr\"\n\t\"github.com/dtm-labs/dtm/dtmsvr/storage\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestWorkflowBranchConflict(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\tstore := dtmsvr.GetStore()\n\tnow := time.Now()\n\tg := &storage.TransGlobalStore{\n\t\tGid:          gid,\n\t\tStatus:       dtmcli.StatusPrepared,\n\t\tNextCronTime: &now,\n\t}\n\terr := store.MaySaveNewTrans(g, []storage.TransBranchStore{\n\t\t{\n\t\t\tBranchID: \"00\",\n\t\t\tOp:       dtmimp.OpAction,\n\t\t},\n\t})\n\tassert.Nil(t, err)\n\terr = dtmimp.CatchP(func() {\n\t\tstore.LockGlobalSaveBranches(gid, dtmcli.StatusPrepared, []storage.TransBranchStore{\n\t\t\t{BranchID: \"00\", Op: dtmimp.OpAction},\n\t\t}, -1)\n\t})\n\tassert.Error(t, err)\n\tstore.ChangeGlobalStatus(g, StatusSucceed, []string{}, true)\n}\n"
  },
  {
    "path": "test/workflow_grpc_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"database/sql\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\t\"github.com/dtm-labs/dtm/client/workflow\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"google.golang.org/grpc/metadata\"\n)\n\nfunc TestWorkflowGrpcSimple(t *testing.T) {\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolGRPC)\n\treq := &busi.ReqGrpc{Amount: 30, TransInResult: \"FAILURE\"}\n\tgid := dtmimp.GetFuncName()\n\tworkflow.Register(gid, func(wf *workflow.Workflow, data []byte) error {\n\t\tvar req busi.ReqGrpc\n\t\tdtmgimp.MustProtoUnmarshal(data, &req)\n\t\t_, err := busi.BusiCli.TransOutBSaga(wf.NewBranchCtx(), &req)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = busi.BusiCli.TransInBSaga(wf.NewBranchCtx(), &req)\n\t\treturn err\n\t})\n\terr := workflow.Execute(gid, gid, dtmgimp.MustProtoMarshal(req))\n\tassert.Error(t, err)\n\tassert.Equal(t, StatusFailed, getTransStatus(gid))\n}\n\nfunc TestWorkflowGrpcRollback(t *testing.T) {\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolGRPC)\n\treq := &busi.ReqGrpc{Amount: 30, TransInResult: \"FAILURE\"}\n\tgid := dtmimp.GetFuncName()\n\tworkflow.Register(gid, func(wf *workflow.Workflow, data []byte) error {\n\t\tvar req busi.ReqGrpc\n\t\tdtmgimp.MustProtoUnmarshal(data, &req)\n\t\twf.NewBranch().OnRollback(func(bb *dtmcli.BranchBarrier) error {\n\t\t\t_, err := busi.BusiCli.TransOutRevertBSaga(wf.Context, &req)\n\t\t\treturn err\n\t\t})\n\t\t_, err := busi.BusiCli.TransOutBSaga(wf.Context, &req)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\twf.NewBranch().OnRollback(func(bb *dtmcli.BranchBarrier) error {\n\t\t\t_, err := busi.BusiCli.TransInRevertBSaga(wf.Context, &req)\n\t\t\treturn err\n\t\t})\n\t\t_, err = busi.BusiCli.TransInBSaga(wf.Context, &req)\n\t\treturn err\n\t})\n\tbefore := getBeforeBalances(\"mysql\")\n\terr := workflow.Execute(gid, gid, dtmgimp.MustProtoMarshal(req))\n\tassert.Error(t, err, dtmcli.ErrFailure)\n\tassert.Equal(t, StatusFailed, getTransStatus(gid))\n\tassertSameBalance(t, before, \"mysql\")\n}\n\nfunc TestWorkflowMixed(t *testing.T) {\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolHTTP)\n\tgid := dtmimp.GetFuncName()\n\terr := workflow.Register(gid, func(wf *workflow.Workflow, data []byte) error {\n\t\tvar req busi.ReqGrpc\n\t\tdtmgimp.MustProtoUnmarshal(data, &req)\n\n\t\t_, err := wf.NewBranch().OnRollback(func(bb *dtmcli.BranchBarrier) error {\n\t\t\t_, err := busi.BusiCli.TransOutRevertBSaga(wf.Context, &req)\n\t\t\treturn err\n\t\t}).Do(func(bb *dtmcli.BranchBarrier) ([]byte, error) {\n\t\t\treturn nil, bb.CallWithDB(dbGet().ToSQLDB(), func(tx *sql.Tx) error {\n\t\t\t\treturn busi.SagaAdjustBalance(tx, busi.TransOutUID, int(-req.Amount), \"\")\n\t\t\t})\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\twf.Context = metadata.NewOutgoingContext(wf.Context, metadata.Pairs(\"k1\", \"v1\"))\n\n\t\treq2 := &busi.ReqHTTP{Amount: int(req.Amount / 2)}\n\t\t_, err = wf.NewBranch().OnCommit(func(bb *dtmcli.BranchBarrier) error {\n\t\t\t_, err := wf.NewRequest().SetBody(req2).Post(Busi + \"/TccBTransInConfirm\")\n\t\t\treturn err\n\t\t}).OnRollback(func(bb *dtmcli.BranchBarrier) error {\n\t\t\t_, err := wf.NewRequest().SetBody(req2).Post(Busi + \"/TccBTransInCancel\")\n\t\t\treturn err\n\t\t}).NewRequest().SetBody(req2).Post(Busi + \"/TccBTransInTry\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = wf.NewBranch().DoXa(busi.BusiConf, func(db *sql.DB) ([]byte, error) {\n\t\t\treturn nil, busi.SagaAdjustBalance(db, busi.TransInUID, int(req.Amount/2), dtmcli.ResultSuccess)\n\t\t})\n\t\treturn err\n\t})\n\tassert.Nil(t, err)\n\tbefore := getBeforeBalances(\"mysql\")\n\treq := &busi.ReqGrpc{Amount: 30}\n\terr = workflow.Execute(gid, gid, dtmgimp.MustProtoMarshal(req))\n\tassert.Nil(t, err)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassertNotSameBalance(t, before, \"mysql\")\n}\n\nfunc TestWorkflowGrpcError(t *testing.T) {\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolGRPC)\n\treq := &busi.ReqGrpc{Amount: 30}\n\tgid := dtmimp.GetFuncName()\n\tbusi.MainSwitch.TransOutResult.SetOnce(\"ERROR\")\n\tworkflow.Register(gid, func(wf *workflow.Workflow, data []byte) error {\n\t\tvar req busi.ReqGrpc\n\t\tdtmgimp.MustProtoUnmarshal(data, &req)\n\t\t_, err := busi.BusiCli.TransOut(wf.NewBranchCtx(), &req)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = busi.BusiCli.TransIn(wf.NewBranchCtx(), &req)\n\t\treturn err\n\t})\n\terr := workflow.Execute(gid, gid, dtmgimp.MustProtoMarshal(req))\n\tassert.Error(t, err)\n\tcronTransOnceForwardCron(t, gid, 1000)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n}\n"
  },
  {
    "path": "test/workflow_http_ret_test.go",
    "content": "package test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/workflow\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestWorkflowRet(t *testing.T) {\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolHTTP)\n\treq := busi.GenReqHTTP(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\n\tworkflow.Register2(gid, func(wf *workflow.Workflow, data []byte) ([]byte, error) {\n\t\tvar req busi.ReqHTTP\n\t\tdtmimp.MustUnmarshal(data, &req)\n\t\t_, err := wf.NewBranch().NewRequest().SetBody(req).Post(Busi + \"/TransOut\")\n\t\treturn []byte(\"result of workflow\"), err\n\t})\n\n\tret, err := workflow.Execute2(gid, gid, dtmimp.MustMarshal(req))\n\tassert.Nil(t, err)\n\tassert.Equal(t, \"result of workflow\", string(ret))\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\n\t// the second execute will return result directly\n\tret, err = workflow.Execute2(gid, gid, dtmimp.MustMarshal(req))\n\tassert.Nil(t, err)\n\tassert.Equal(t, \"result of workflow\", string(ret))\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n}\n"
  },
  {
    "path": "test/workflow_http_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"database/sql\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/workflow\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestWorkflowNormal(t *testing.T) {\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolHTTP)\n\treq := busi.GenReqHTTP(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\n\tworkflow.Register(gid, func(wf *workflow.Workflow, data []byte) error {\n\t\twf.NewBranch().OnFinish(func(bb *dtmcli.BranchBarrier, isRollback bool) error {\n\t\t\tlogger.Debugf(\"OnFinish isRollback: %v\", isRollback)\n\t\t\treturn nil\n\t\t})\n\t\tvar req busi.ReqHTTP\n\t\tdtmimp.MustUnmarshal(data, &req)\n\t\t_, err := wf.NewBranch().NewRequest().SetBody(req).Post(Busi + \"/TransOut\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = wf.NewBranch().NewRequest().SetBody(req).Post(Busi + \"/TransIn\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n\n\terr := workflow.Execute(gid, gid, dtmimp.MustMarshal(req))\n\tassert.Nil(t, err)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n}\n\nfunc TestWorkflowRollback(t *testing.T) {\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolHTTP)\n\n\treq := &busi.ReqHTTP{Amount: 30, TransInResult: dtmimp.ResultFailure}\n\tgid := dtmimp.GetFuncName()\n\n\tworkflow.Register(gid, func(wf *workflow.Workflow, data []byte) error {\n\t\twf.NewBranch().OnFinish(func(bb *dtmcli.BranchBarrier, isRollback bool) error {\n\t\t\tlogger.Debugf(\"OnFinish isRollback: %v\", isRollback)\n\t\t\treturn nil\n\t\t})\n\t\tvar req busi.ReqHTTP\n\t\tdtmimp.MustUnmarshal(data, &req)\n\t\t_, err := wf.NewBranch().OnRollback(func(bb *dtmcli.BranchBarrier) error {\n\t\t\t_, err := wf.NewRequest().SetBody(req).Post(Busi + \"/SagaBTransOutCom\")\n\t\t\treturn err\n\t\t}).Do(func(bb *dtmcli.BranchBarrier) ([]byte, error) {\n\t\t\treturn nil, bb.CallWithDB(dbGet().ToSQLDB(), func(tx *sql.Tx) error {\n\t\t\t\treturn busi.SagaAdjustBalance(tx, busi.TransOutUID, -req.Amount, \"\")\n\t\t\t})\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = wf.NewBranch().OnRollback(func(bb *dtmcli.BranchBarrier) error {\n\t\t\treturn bb.CallWithDB(dbGet().ToSQLDB(), func(tx *sql.Tx) error {\n\t\t\t\treturn busi.SagaAdjustBalance(tx, busi.TransInUID, -req.Amount, \"\")\n\t\t\t})\n\t\t}).NewRequest().SetBody(req).Post(Busi + \"/SagaBTransIn\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n\tbefore := getBeforeBalances(\"mysql\")\n\n\terr := workflow.Execute(gid, gid, dtmimp.MustMarshal(req))\n\tassert.Error(t, err, dtmcli.ErrFailure)\n\tassert.Equal(t, StatusFailed, getTransStatus(gid))\n\tassertSameBalance(t, before, \"mysql\")\n}\n\nfunc TestWorkflowTcc(t *testing.T) {\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolHTTP)\n\treq := busi.GenReqHTTP(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\n\tworkflow.Register(gid, func(wf *workflow.Workflow, data []byte) error {\n\t\tvar req busi.ReqHTTP\n\t\tdtmimp.MustUnmarshal(data, &req)\n\t\t_, err := wf.NewBranch().OnRollback(func(bb *dtmcli.BranchBarrier) error {\n\t\t\t_, err := wf.NewRequest().SetBody(req).Post(Busi + \"/TccBTransOutCancel\")\n\t\t\treturn err\n\t\t}).OnCommit(func(bb *dtmcli.BranchBarrier) error {\n\t\t\t_, err := wf.NewRequest().SetBody(req).Post(Busi + \"/TccBTransOutConfirm\")\n\t\t\treturn err\n\t\t}).NewRequest().SetBody(req).Post(Busi + \"/TccBTransOutTry\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = wf.NewBranch().OnRollback(func(bb *dtmcli.BranchBarrier) error {\n\t\t\t_, err := wf.NewRequest().SetBody(req).Post(Busi + \"/TccBTransInCancel\")\n\t\t\treturn err\n\t\t}).OnCommit(func(bb *dtmcli.BranchBarrier) error {\n\t\t\t_, err := wf.NewRequest().SetBody(req).Post(Busi + \"/TccBTransInConfirm\")\n\t\t\treturn err\n\t\t}).NewRequest().SetBody(req).Post(Busi + \"/TccBTransInTry\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n\n\tbefore := getBeforeBalances(\"mysql\")\n\terr := workflow.Execute(gid, gid, dtmimp.MustMarshal(req))\n\tassert.Nil(t, err)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassertNotSameBalance(t, before, \"mysql\")\n}\n\nfunc TestWorkflowTccRollback(t *testing.T) {\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolHTTP)\n\treq := busi.GenReqHTTP(30, false, true)\n\tgid := dtmimp.GetFuncName()\n\n\tworkflow.Register(gid, func(wf *workflow.Workflow, data []byte) error {\n\t\tvar req busi.ReqHTTP\n\t\tdtmimp.MustUnmarshal(data, &req)\n\t\t_, err := wf.NewBranch().OnRollback(func(bb *dtmcli.BranchBarrier) error {\n\t\t\t_, err := wf.NewRequest().SetBody(req).Post(Busi + \"/TccBTransOutCancel\")\n\t\t\treturn err\n\t\t}).OnCommit(func(bb *dtmcli.BranchBarrier) error {\n\t\t\t_, err := wf.NewRequest().SetBody(req).Post(Busi + \"/TccBTransOutConfirm\")\n\t\t\treturn err\n\t\t}).NewRequest().SetBody(req).Post(Busi + \"/TccBTransOutTry\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = wf.NewBranch().OnRollback(func(bb *dtmcli.BranchBarrier) error {\n\t\t\t_, err := wf.NewRequest().SetBody(req).Post(Busi + \"/TccBTransInCancel\")\n\t\t\treturn err\n\t\t}).OnCommit(func(bb *dtmcli.BranchBarrier) error {\n\t\t\t_, err := wf.NewRequest().SetBody(req).Post(Busi + \"/TccBTransInConfirm\")\n\t\t\treturn err\n\t\t}).NewRequest().SetBody(req).Post(Busi + \"/TccBTransInTry\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n\n\tbefore := getBeforeBalances(\"mysql\")\n\terr := workflow.Execute(gid, gid, dtmimp.MustMarshal(req))\n\tassert.Error(t, err)\n\tassert.Equal(t, StatusFailed, getTransStatus(gid))\n\tassertSameBalance(t, before, \"mysql\")\n}\n\nfunc TestWorkflowError(t *testing.T) {\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolHTTP)\n\treq := busi.GenReqHTTP(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\tbusi.MainSwitch.TransOutResult.SetOnce(\"ERROR\")\n\n\tworkflow.Register(gid, func(wf *workflow.Workflow, data []byte) error {\n\t\tvar req busi.ReqHTTP\n\t\tdtmimp.MustUnmarshal(data, &req)\n\t\t_, err := wf.NewBranch().NewRequest().SetBody(req).Post(Busi + \"/TransOut\")\n\t\treturn err\n\t})\n\n\terr := workflow.Execute(gid, gid, dtmimp.MustMarshal(req))\n\tassert.Error(t, err)\n\tcronTransOnceForwardCron(t, gid, 1000)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n}\n\nfunc TestWorkflowOngoing(t *testing.T) {\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolHTTP)\n\treq := busi.GenReqHTTP(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\tbusi.MainSwitch.TransOutResult.SetOnce(\"ONGOING\")\n\n\tworkflow.Register(gid, func(wf *workflow.Workflow, data []byte) error {\n\t\tvar req busi.ReqHTTP\n\t\tdtmimp.MustUnmarshal(data, &req)\n\t\t_, err := wf.NewBranch().NewRequest().SetBody(req).Post(Busi + \"/TransOut\")\n\t\treturn err\n\t})\n\n\terr := workflow.Execute(gid, gid, dtmimp.MustMarshal(req))\n\tassert.Error(t, err)\n\tcronTransOnceForwardCron(t, gid, 1000)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n}\n\nvar resumeCounter int\n\nfunc TestWorkflowResumeSkip(t *testing.T) {\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolHTTP)\n\treq := busi.GenReqHTTP(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\n\tresumeCounter = 0\n\tbusi.MainSwitch.TransOutResult.SetOnce(\"ONGOING\")\n\n\tworkflow.Register(gid, func(wf *workflow.Workflow, data []byte) error {\n\t\twf.NewBranch().Do(func(bb *dtmcli.BranchBarrier) ([]byte, error) {\n\t\t\tlogger.Infof(\"increase resume counter\")\n\t\t\tresumeCounter++\n\t\t\treturn nil, nil\n\t\t})\n\t\tvar req busi.ReqHTTP\n\t\tdtmimp.MustUnmarshal(data, &req)\n\t\t_, err := wf.NewBranch().NewRequest().SetBody(req).Post(Busi + \"/TransOut\")\n\t\treturn err\n\t})\n\n\terr := workflow.Execute(gid, gid, dtmimp.MustMarshal(req))\n\tassert.Error(t, err)\n\tcronTransOnceForwardCron(t, gid, 1000)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassert.Equal(t, 1, resumeCounter)\n}\n"
  },
  {
    "path": "test/workflow_interceptor_test.go",
    "content": "package test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/workflow\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"google.golang.org/grpc\"\n)\n\nfunc TestWorkflowInterceptorOutsideSaga(t *testing.T) {\n\tcalled := false\n\tworkflow.Interceptor(context.TODO(), \"method\", nil, nil, &grpc.ClientConn{}, func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, opts ...grpc.CallOption) error {\n\t\tcalled = true\n\t\treturn nil\n\t})\n\tassert.True(t, called)\n}\n"
  },
  {
    "path": "test/workflow_ongoing_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"database/sql\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp\"\n\t\"github.com/dtm-labs/dtm/client/workflow\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar ongoingStep = 0\n\nfunc fetchOngoingStep(dest int) bool {\n\tc := ongoingStep\n\tlogger.Debugf(\"ongoing step is: %d\", c)\n\tif c == dest {\n\t\tongoingStep++\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc TestWorkflowSimpleResume(t *testing.T) {\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolHTTP)\n\treq := busi.GenReqHTTP(30, false, false)\n\tgid := dtmimp.GetFuncName()\n\tongoingStep = 0\n\n\tworkflow.Register(gid, func(wf *workflow.Workflow, data []byte) error {\n\t\tif fetchOngoingStep(0) {\n\t\t\treturn dtmcli.ErrOngoing\n\t\t}\n\t\tvar req busi.ReqHTTP\n\t\tdtmimp.MustUnmarshal(data, &req)\n\t\t_, err := wf.NewBranch().NewRequest().SetBody(req).Post(Busi + \"/TransOut\")\n\t\treturn err\n\t})\n\n\terr := workflow.Execute(gid, gid, dtmimp.MustMarshal(req))\n\tassert.Error(t, err)\n\tcronTransOnceForwardNow(t, gid, 1000)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n}\n\nfunc TestWorkflowGrpcRollbackResume(t *testing.T) {\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolGRPC)\n\tgid := dtmimp.GetFuncName()\n\tongoingStep = 0\n\tworkflow.Register(gid, func(wf *workflow.Workflow, data []byte) error {\n\t\tvar req busi.ReqGrpc\n\t\tdtmgimp.MustProtoUnmarshal(data, &req)\n\t\tif fetchOngoingStep(0) {\n\t\t\treturn dtmcli.ErrOngoing\n\t\t}\n\t\twf.NewBranch().OnRollback(func(bb *dtmcli.BranchBarrier) error {\n\t\t\tif fetchOngoingStep(4) {\n\t\t\t\treturn dtmcli.ErrOngoing\n\t\t\t}\n\t\t\t_, err := busi.BusiCli.TransOutRevertBSaga(wf.Context, &req)\n\t\t\treturn err\n\t\t})\n\t\t_, err := busi.BusiCli.TransOutBSaga(wf.Context, &req)\n\t\tif fetchOngoingStep(1) {\n\t\t\treturn dtmcli.ErrOngoing\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\twf.NewBranch().OnRollback(func(bb *dtmcli.BranchBarrier) error {\n\t\t\tif fetchOngoingStep(3) {\n\t\t\t\treturn dtmcli.ErrOngoing\n\t\t\t}\n\t\t\t_, err := busi.BusiCli.TransInRevertBSaga(wf.Context, &req)\n\t\t\treturn err\n\t\t})\n\t\t_, err = busi.BusiCli.TransInBSaga(wf.Context, &req)\n\t\tif fetchOngoingStep(2) {\n\t\t\treturn dtmcli.ErrOngoing\n\t\t}\n\t\treturn err\n\t}, func(wf *workflow.Workflow) {\n\t\twf.Options.CompensateErrorBranch = true\n\t})\n\tbefore := getBeforeBalances(\"mysql\")\n\treq := &busi.ReqGrpc{Amount: 30, TransInResult: \"FAILURE\"}\n\terr := workflow.Execute(gid, gid, dtmgimp.MustProtoMarshal(req))\n\tassert.Error(t, err, dtmcli.ErrOngoing)\n\tassert.Equal(t, StatusPrepared, getTransStatus(gid))\n\tcronTransOnceForwardNow(t, gid, 1000)\n\tassert.Equal(t, StatusPrepared, getTransStatus(gid))\n\tcronTransOnceForwardNow(t, gid, 1000)\n\tassert.Equal(t, StatusPrepared, getTransStatus(gid))\n\tcronTransOnceForwardNow(t, gid, 1000)\n\tassert.Equal(t, StatusPrepared, getTransStatus(gid))\n\tcronTransOnceForwardNow(t, gid, 1000)\n\tassert.Equal(t, StatusPrepared, getTransStatus(gid))\n\tcronTransOnceForwardNow(t, gid, 1000)\n\tassert.Equal(t, StatusFailed, getTransStatus(gid))\n\tassertSameBalance(t, before, \"mysql\")\n}\n\nfunc TestWorkflowXaResume(t *testing.T) {\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolGRPC)\n\tongoingStep = 0\n\tgid := dtmimp.GetFuncName()\n\tworkflow.Register(gid, func(wf *workflow.Workflow, data []byte) error {\n\t\t_, err := wf.NewBranch().DoXa(busi.BusiConf, func(db *sql.DB) ([]byte, error) {\n\t\t\tif fetchOngoingStep(0) {\n\t\t\t\treturn nil, dtmcli.ErrOngoing\n\t\t\t}\n\t\t\treturn nil, busi.SagaAdjustBalance(db, busi.TransOutUID, -30, dtmcli.ResultSuccess)\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = wf.NewBranch().DoXa(busi.BusiConf, func(db *sql.DB) ([]byte, error) {\n\t\t\tif fetchOngoingStep(1) {\n\t\t\t\treturn nil, dtmcli.ErrOngoing\n\t\t\t}\n\t\t\treturn nil, busi.SagaAdjustBalance(db, busi.TransInUID, 30, dtmcli.ResultSuccess)\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif fetchOngoingStep(2) {\n\t\t\treturn dtmcli.ErrOngoing\n\t\t}\n\n\t\treturn err\n\t})\n\tbefore := getBeforeBalances(\"mysql\")\n\terr := workflow.Execute(gid, gid, nil)\n\tassert.Equal(t, dtmcli.ErrOngoing, err)\n\n\tcronTransOnceForwardNow(t, gid, 1000)\n\tassert.Equal(t, StatusPrepared, getTransStatus(gid))\n\tcronTransOnceForwardNow(t, gid, 1000)\n\tassert.Equal(t, StatusPrepared, getTransStatus(gid))\n\tcronTransOnceForwardNow(t, gid, 1000)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassertNotSameBalance(t, before, \"mysql\")\n}\n"
  },
  {
    "path": "test/workflow_xa_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"database/sql\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/workflow\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/dtm-labs/logger\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestWorkflowXaAction(t *testing.T) {\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolGRPC)\n\tgid := dtmimp.GetFuncName()\n\tworkflow.Register(gid, func(wf *workflow.Workflow, data []byte) error {\n\t\t_, err := wf.NewBranch().DoXa(busi.BusiConf, func(db *sql.DB) ([]byte, error) {\n\t\t\treturn nil, busi.SagaAdjustBalance(db, busi.TransOutUID, -30, dtmcli.ResultSuccess)\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = wf.NewBranch().DoXa(busi.BusiConf, func(db *sql.DB) ([]byte, error) {\n\t\t\treturn nil, busi.SagaAdjustBalance(db, busi.TransInUID, 30, dtmcli.ResultSuccess)\n\t\t})\n\t\treturn err\n\t})\n\tbefore := getBeforeBalances(\"mysql\")\n\terr := workflow.Execute(gid, gid, nil)\n\tassert.Nil(t, err)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassertNotSameBalance(t, before, \"mysql\")\n}\n\nfunc TestWorkflowXaRollback(t *testing.T) {\n\tworkflow.SetProtocolForTest(dtmimp.ProtocolGRPC)\n\tgid := dtmimp.GetFuncName()\n\tworkflow.Register(gid, func(wf *workflow.Workflow, data []byte) error {\n\t\t_, err := wf.NewBranch().DoXa(busi.BusiConf, func(db *sql.DB) ([]byte, error) {\n\t\t\treturn nil, busi.SagaAdjustBalance(db, busi.TransOutUID, -30, dtmcli.ResultSuccess)\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = wf.NewBranch().DoXa(busi.BusiConf, func(db *sql.DB) ([]byte, error) {\n\t\t\te := busi.SagaAdjustBalance(db, busi.TransInUID, 30, dtmcli.ResultSuccess)\n\t\t\tlogger.FatalIfError(e)\n\t\t\treturn nil, dtmcli.ErrFailure\n\t\t})\n\t\treturn err\n\t})\n\tbefore := getBeforeBalances(\"mysql\")\n\terr := workflow.Execute(gid, gid, nil)\n\tassert.Equal(t, dtmcli.ErrFailure, err)\n\tassert.Equal(t, StatusFailed, getTransStatus(gid))\n\tassertSameBalance(t, before, \"mysql\")\n}\n"
  },
  {
    "path": "test/xa_cover_test.go",
    "content": "package test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/go-resty/resty/v2\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestXaCoverDBError(t *testing.T) {\n\toldDriver := busi.BusiConf.Driver\n\tgid := dtmimp.GetFuncName()\n\terr := dtmcli.XaGlobalTransaction(DtmServer, gid, func(xa *dtmcli.Xa) (*resty.Response, error) {\n\t\treq := busi.GenReqHTTP(30, false, false)\n\t\t_, err := xa.CallBranch(req, busi.Busi+\"/TransOutXa\")\n\t\tassert.Nil(t, err)\n\t\tbusi.BusiConf.Driver = \"no-driver\"\n\t\t_, err = xa.CallBranch(req, busi.Busi+\"/TransInXa\")\n\t\tassert.Error(t, err)\n\t\treturn nil, err\n\t})\n\tassert.Error(t, err)\n\twaitTransProcessed(gid)\n\tbusi.BusiConf.Driver = oldDriver\n\tcronTransOnceForwardNow(t, gid, 500) // rollback succeeded here\n\tassert.Equal(t, StatusFailed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusSucceed, StatusPrepared}, getBranchesStatus(gid))\n}\n\nfunc TestXaCoverDTMError(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\terr := dtmcli.XaGlobalTransaction(\"localhost:01\", gid, func(xa *dtmcli.Xa) (*resty.Response, error) {\n\t\treturn nil, nil\n\t})\n\tassert.Error(t, err)\n}\n\nfunc TestXaCoverGidError(t *testing.T) {\n\tif dtmimp.GetCurrentDBType() != dtmimp.DBTypeMysql {\n\t\treturn\n\t}\n\tgid := dtmimp.GetFuncName() + \"-'  '\"\n\terr := dtmcli.XaGlobalTransaction(DtmServer, gid, func(xa *dtmcli.Xa) (*resty.Response, error) {\n\t\treq := busi.GenReqHTTP(30, false, false)\n\t\t_, err := xa.CallBranch(req, busi.Busi+\"/TransOutXa\")\n\t\tassert.Error(t, err)\n\t\treturn nil, err\n\t})\n\tassert.Error(t, err)\n\twaitTransProcessed(gid)\n}\n"
  },
  {
    "path": "test/xa_grpc_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/client/dtmgrpc\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"google.golang.org/protobuf/types/known/emptypb\"\n)\n\nfunc TestXaGrpcNormal(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\terr := dtmgrpc.XaGlobalTransaction(DtmGrpcServer, gid, func(xa *dtmgrpc.XaGrpc) error {\n\t\treq := busi.GenReqGrpc(30, false, false)\n\t\tr := &emptypb.Empty{}\n\t\terr := xa.CallBranch(req, busi.BusiGrpc+\"/busi.Busi/TransOutXa\", r)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn xa.CallBranch(req, busi.BusiGrpc+\"/busi.Busi/TransInXa\", r)\n\t})\n\tassert.Equal(t, nil, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid))\n}\n\nfunc TestXaGrpcRollback(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\terr := dtmgrpc.XaGlobalTransaction(DtmGrpcServer, gid, func(xa *dtmgrpc.XaGrpc) error {\n\t\treq := busi.GenReqGrpc(30, false, true)\n\t\tr := &emptypb.Empty{}\n\t\terr := xa.CallBranch(req, busi.BusiGrpc+\"/busi.Busi/TransOutXa\", r)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn xa.CallBranch(req, busi.BusiGrpc+\"/busi.Busi/TransInXa\", r)\n\t})\n\tassert.Error(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, []string{StatusSucceed, StatusPrepared}, getBranchesStatus(gid))\n\tassert.Equal(t, StatusFailed, getTransStatus(gid))\n}\n\nfunc TestXaGrpcType(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\t_, err := dtmgrpc.XaGrpcFromRequest(context.Background())\n\tassert.Error(t, err)\n\n\terr = dtmgrpc.XaLocalTransaction(context.Background(), busi.BusiConf, nil)\n\tassert.Error(t, err)\n\n\terr = dtmimp.CatchP(func() {\n\t\tdtmgrpc.XaGlobalTransaction(DtmGrpcServer, gid, func(xa *dtmgrpc.XaGrpc) error { panic(fmt.Errorf(\"hello\")) })\n\t})\n\tassert.Error(t, err)\n\twaitTransProcessed(gid)\n}\n\nfunc TestXaGrpcLocalError(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\terr := dtmgrpc.XaGlobalTransaction(DtmGrpcServer, gid, func(xa *dtmgrpc.XaGrpc) error {\n\t\treturn fmt.Errorf(\"an error\")\n\t})\n\tassert.Error(t, err, fmt.Errorf(\"an error\"))\n\twaitTransProcessed(gid)\n}\n"
  },
  {
    "path": "test/xa_test.go",
    "content": "/*\n * Copyright (c) 2021 yedf. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/dtm-labs/dtm/client/dtmcli\"\n\t\"github.com/dtm-labs/dtm/client/dtmcli/dtmimp\"\n\t\"github.com/dtm-labs/dtm/dtmutil\"\n\t\"github.com/dtm-labs/dtm/test/busi\"\n\t\"github.com/go-resty/resty/v2\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestXaNormal(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\terr := dtmcli.XaGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(xa *dtmcli.Xa) (*resty.Response, error) {\n\t\treq := busi.GenReqHTTP(30, false, false)\n\t\tresp, err := xa.CallBranch(req, busi.Busi+\"/TransOutXa\")\n\t\tif err != nil {\n\t\t\treturn resp, err\n\t\t}\n\t\treturn xa.CallBranch(req, busi.Busi+\"/TransInXa\")\n\t})\n\tassert.Equal(t, nil, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid))\n}\n\nfunc TestXaDuplicate(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\terr := dtmcli.XaGlobalTransaction(DtmServer, gid, func(xa *dtmcli.Xa) (*resty.Response, error) {\n\t\treq := busi.GenReqHTTP(30, false, false)\n\t\t_, err := xa.CallBranch(req, busi.Busi+\"/TransOutXa\")\n\t\tassert.Nil(t, err)\n\t\tsdb, err := dtmimp.StandaloneDB(busi.BusiConf)\n\t\tassert.Nil(t, err)\n\t\tif dtmcli.GetCurrentDBType() == dtmcli.DBTypeMysql {\n\t\t\t_, err = dtmimp.DBExec(busi.BusiConf.Driver, sdb, \"xa recover\")\n\t\t\tassert.Nil(t, err)\n\t\t}\n\t\t_, err = dtmimp.DBExec(busi.BusiConf.Driver, sdb, dtmimp.GetDBSpecial(busi.BusiConf.Driver).GetXaSQL(\"commit\", gid+\"-01\")) // simulate repeated request\n\t\tassert.Nil(t, err)\n\t\treturn xa.CallBranch(req, busi.Busi+\"/TransInXa\")\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid))\n}\n\nfunc TestXaRollback(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\terr := dtmcli.XaGlobalTransaction(DtmServer, gid, func(xa *dtmcli.Xa) (*resty.Response, error) {\n\t\treq := busi.GenReqHTTP(30, false, true)\n\t\tresp, err := xa.CallBranch(req, busi.Busi+\"/TransOutXa\")\n\t\tif err != nil {\n\t\t\treturn resp, err\n\t\t}\n\t\treturn xa.CallBranch(req, busi.Busi+\"/TransInXa\")\n\t})\n\tassert.Error(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, []string{StatusSucceed, StatusPrepared}, getBranchesStatus(gid))\n\tassert.Equal(t, StatusFailed, getTransStatus(gid))\n}\n\nfunc TestXaLocalError(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\terr := dtmcli.XaGlobalTransaction(DtmServer, gid, func(xa *dtmcli.Xa) (*resty.Response, error) {\n\t\treturn nil, fmt.Errorf(\"an error\")\n\t})\n\tassert.Error(t, err, fmt.Errorf(\"an error\"))\n\twaitTransProcessed(gid)\n}\n\nfunc TestXaTimeout(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\ttimeoutChan := make(chan int, 1)\n\terr := dtmcli.XaGlobalTransaction(DtmServer, gid, func(xa *dtmcli.Xa) (*resty.Response, error) {\n\t\tgo func() {\n\t\t\tcronTransOnceForwardNow(t, gid, 300)\n\t\t\ttimeoutChan <- 0\n\t\t}()\n\t\t<-timeoutChan\n\t\treturn nil, nil\n\t})\n\tassert.Error(t, err)\n\tassert.Equal(t, StatusFailed, getTransStatus(gid))\n\tassert.Regexp(t, `^Timeout after \\d+ seconds$`, getTrans(gid).RollbackReason)\n\tassert.Equal(t, []string{}, getBranchesStatus(gid))\n}\n\nfunc TestXaNotTimeout(t *testing.T) {\n\tgid := dtmimp.GetFuncName()\n\ttimeoutChan := make(chan int, 1)\n\terr := dtmcli.XaGlobalTransaction(DtmServer, gid, func(xa *dtmcli.Xa) (*resty.Response, error) {\n\t\tgo func() {\n\t\t\tcronTransOnceForwardNow(t, gid, 0) // not timeout,\n\t\t\ttimeoutChan <- 0\n\t\t}()\n\t\t<-timeoutChan\n\t\treq := busi.GenReqHTTP(30, false, false)\n\t\t_, err := xa.CallBranch(req, busi.Busi+\"/TransOutXa\")\n\t\tassert.Nil(t, err)\n\t\tbusi.MainSwitch.NextResult.SetOnce(dtmcli.ResultOngoing) // make commit temp error\n\t\treturn nil, nil\n\t})\n\tassert.Nil(t, err)\n\twaitTransProcessed(gid)\n\tassert.Equal(t, StatusSubmitted, getTransStatus(gid))\n\tcronTransOnce(t, gid)\n\tassert.Equal(t, StatusSucceed, getTransStatus(gid))\n\tassert.Equal(t, []string{StatusPrepared, StatusSucceed}, getBranchesStatus(gid))\n}\n"
  }
]