├── .babelrc ├── .dockerignore ├── .eslintrc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── new_feature.md ├── dependabot.yml ├── stale.yml └── workflows │ └── node.js.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.MD ├── README_ZH.MD ├── andLogic.rego ├── app.js ├── bin └── www ├── config.yaml ├── data ├── .gitkeep └── rs.csv ├── doc ├── HowToConfigFabricParameters.md ├── HowToConfigFabricParameters_ZH.md ├── ProbConfigMsg.png ├── Probe.png ├── ProbeUI.png ├── batchtimout.png ├── maxMsgCount.png ├── quick_sample.png └── quicksample2.png ├── e2e ├── minifab.test.js └── testnetwork.test.js ├── lib ├── commands.js ├── fileIO.js └── libs.js ├── monitor ├── docker-compose.yaml └── prometheus │ ├── grafana.json │ ├── grafana │ ├── conf │ │ ├── defaults.ini │ │ ├── ldap.toml │ │ ├── ldap_multiple.toml │ │ ├── provisioning │ │ │ ├── dashboards │ │ │ │ └── sample.yaml │ │ │ ├── datasources │ │ │ │ └── sample.yaml │ │ │ └── notifiers │ │ │ │ └── sample.yaml │ │ └── sample.ini │ └── grafana │ │ └── grafana.db │ ├── prometheus.yml │ └── scripts ├── nyc.config.js ├── package-lock.json ├── package.json ├── public ├── javascripts │ ├── index │ │ ├── bundle.js │ │ └── bundle.js.LICENSE.txt │ ├── indexAbsoluteMaxBytes.js │ ├── indexBatchTimeout.js │ ├── indexMaxMessageCount.js │ ├── indexPreferredMaxBytes.js │ ├── newUI │ │ ├── bundle.js │ │ └── bundle.js.LICENSE.txt │ └── sidebar │ │ ├── bundle.js │ │ └── bundle.js.LICENSE.txt ├── locales │ ├── en │ │ └── translation.json │ └── zh │ │ └── translation.json └── stylesheets │ └── style.css ├── routes ├── api.js ├── newUI.js ├── quick.js └── result.js ├── sample ├── caliper │ ├── myAssetBenchmark.yaml │ ├── networkConfig.yaml │ └── readAsset.js ├── minifab │ ├── andLogic.rego │ ├── config.yaml │ └── prepareConfig.sh └── test-network │ ├── andLogic.rego │ ├── config.yaml │ ├── docker-compose-test-net.yaml │ └── prepareConfig.sh ├── src ├── index │ └── App.jsx ├── newUI │ ├── App.jsx │ ├── actions │ │ └── index.js │ ├── components │ │ ├── MyImportExport.jsx │ │ ├── MyInput.jsx │ │ └── MyList.jsx │ └── reducers │ │ ├── index.js │ │ └── todos.js └── sidebar │ ├── App.jsx │ └── components │ └── SideBar.jsx ├── stop.sh ├── test ├── app.test.js ├── commands.test.js ├── fileIO.test.js └── lib.test.js ├── views ├── config │ └── index.html ├── newUI │ └── index.html ├── resultAbsoluteMaxBytes │ └── index.html ├── resultBatchTimeout │ └── index.html ├── resultMaxMessageCount │ └── index.html └── resultPreferredMaxBytes │ └── index.html └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env","@babel/preset-react"] 3 | } -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | caliper-workspace 2 | coverage 3 | fabric-samples 4 | minifab 5 | node_modules 6 | tmp 7 | caliper.log 8 | tape 9 | doc 10 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true, 5 | "mocha": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "parserOptions": { 9 | "ecmaVersion": 8, 10 | "sourceType": "module" 11 | }, 12 | "rules": { 13 | "array-bracket-spacing": ["error", "never"], 14 | "arrow-spacing": ["error"], 15 | "brace-style": ["error"], 16 | "comma-spacing": ["error"], 17 | "curly": ["error"], 18 | "dot-notation": ["error"], 19 | "eqeqeq": ["error"], 20 | "indent": [ 21 | "error", 22 | 4, 23 | { 24 | "SwitchCase": 1 25 | } 26 | ], 27 | "keyword-spacing":"error", 28 | "linebreak-style": ["error", "unix"], 29 | "max-len": [ 30 | "error", 31 | { 32 | "code": 150, 33 | "ignoreTrailingComments": true, 34 | "ignoreUrls": true, 35 | "ignoreStrings": true, 36 | "ignoreTemplateLiterals": true, 37 | "ignoreRegExpLiterals": true 38 | } 39 | ], 40 | "no-console": ["warn"], 41 | "no-shadow": ["error"], 42 | "no-throw-literal": ["error"], 43 | "no-trailing-spaces": ["error"], 44 | "no-useless-call": ["error"], 45 | "no-unused-vars": [ 46 | "error", 47 | { 48 | "args": "none" 49 | } 50 | ], 51 | "no-var": ["error"], 52 | "no-with": ["error"], 53 | "object-curly-spacing": ["error", "never"], 54 | "operator-linebreak": ["error"], 55 | "prefer-const": ["error"], 56 | "quotes": ["error", "single"], 57 | "semi": ["error", "always"], 58 | "spaced-comment": ["error", "always"], 59 | "space-before-blocks": [ 60 | "error", 61 | { 62 | "functions": "always", 63 | "keywords": "always", 64 | "classes": "always" 65 | } 66 | ], 67 | "space-infix-ops": ["error"], 68 | "space-in-parens": ["error", "never"], 69 | "yoda": "error" 70 | } 71 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 16 | 17 | **Describe the bug** 18 | A clear and concise description of what the bug is. 19 | 20 | **To Reproduce** 21 | Steps to reproduce the behavior: 22 | 1. 23 | 2. 24 | 3. 25 | 26 | **Expected behavior** 27 | A clear and concise description of what you expected to happen. 28 | 29 | **Logs** 30 | Attach logs from Probe here. **And please format them with markdown!** 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/new_feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New Feature 3 | about: Request a new feature request 4 | title: '' 5 | labels: Feature 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Is your proposal related to a problem? 11 | 12 | 16 | 17 | (Write your answer here.) 18 | 19 | ### Describe the solution you'd like 20 | 21 | 28 | 29 | (Describe your proposed solution here.) 30 | 31 | ### Describe alternatives you've considered 32 | 33 | 36 | 37 | (Write your answer here.) 38 | 39 | ### Additional context 40 | 41 | 45 | 46 | (Write your answer here.) 47 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: '/' 5 | schedule: 6 | interval: daily 7 | groups: 8 | npm-dependencies: 9 | patterns: 10 | - "*" 11 | open-pull-requests-limit: 10 12 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 30 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: stale 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false 18 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [16.x, 18.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: 'npm' 29 | - run: npm ci --legacy-peer-deps 30 | - run: npm test --legacy-peer-deps 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | vars 3 | spec.yaml 4 | minifabric 5 | fabric-samples* 6 | coverage 7 | .nyc_output 8 | .vscode 9 | nohup.out 10 | log.txt 11 | logger.log 12 | tmp 13 | data/rs.csv 14 | caliper-workspace/ 15 | caliper.log -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | LABEL maintainer="yy19902439@126.com" 4 | 5 | #RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories 6 | 7 | RUN apk add --no-cache bash docker-cli nodejs npm 8 | 9 | RUN mkdir -p /home/probe 10 | COPY . /home/probe 11 | 12 | WORKDIR /home/probe 13 | RUN npm install 14 | 15 | EXPOSE 3000 16 | 17 | CMD npm start -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # Probe 2 | 3 | [![Build Status](https://dev.azure.com/yy19902439/yy19902439/_apis/build/status/SamYuan1990.Probe?branchName=main)](https://dev.azure.com/yy19902439/yy19902439/_build/latest?definitionId=8&branchName=main) 4 | 5 | [![Issues](https://img.shields.io/github/issues/SamYuan1990/Probe?color=0088ff)](https://github.com/SamYuan1990/Probe/issues) 6 | 7 | [![GitHub pull requests](https://img.shields.io/github/issues-pr/SamYuan1990/Probe?color=0088ff)](https://github.com/SamYuan1990/Probe/pulls) 8 | 9 | [中文](README_ZH.MD) 10 | 11 | 12 | ## What is Probe 13 | 14 | ![Probe](/doc/Probe.png) 15 | 16 | According to [blockchain-performance-metrics](https://www.hyperledger.org/learn/publications/blockchain-performance-metrics), Probe is a web GUI application for Hyperledger Fabric mantianer, user, research. Recently, aims at providing a way to control both SUT and LGC to find the best block config logic for specific chaincode for specific fabric network. Meanwhile has a better understanding of how block config impacts performance. 17 | 18 | - Probe provides loop test control for given block parameter arrays. 19 | - Probe provides TPS result review via GUI. 20 | - Probe provides sample chaincode for some test cases. 21 | 22 | Long term goal for Probe is a coordinator between Test Harness or LGC, SUT. 23 | 24 | - Probe will allow you design shell scripts to schedule SUT and Test Harness. 25 | - Probe will allow you investigate performance matrix with GUI in customer way. 26 | 27 | **You can use Probe to ...**[HowToConfigFabricParameters](doc/HowToConfigFabricParameters.md) 28 | 29 | --- 30 | **Sample run of [Probe](https://www.bilibili.com/video/BV1Kz4y1179L)** 31 | 32 | --- 33 | 34 | ## Table Of Content 35 | 36 | * [Prerequisites](#prerequisites) 37 | * [Quick_Start](#Quick_Start) 38 | * [Contributing](#contributing) 39 | * [License](#license) 40 | * [Contact](#contact) 41 | * [Regards](#thanks-for-choosing) 42 | 43 | --- 44 | ## Prerequisites 45 | 1. Install this project `npm install` 46 | 1. Install fabric-sample environment 47 | `curl -vsS https://raw.githubusercontent.com/hyperledger/fabric/master/scripts/bootstrap.sh | bash` 48 | **Note** 49 | If the above curl command fails, it may be that the old version of curl cannot handle redirection or cannot be redirected due to network reasons in 50 | 51 | some countries and regions. At this time, users can download the [bootstrap.sh](https://raw.githubusercontent.com/hyperledger/fabric/master/scripts/bootstrap.sh) script then run it. 52 | 1. Get tape `docker pull ghcr.io/hyperledger-twgc/tape` 53 | 1. Apply the bridge file to adjust block parameters for test network `cp sample/prepareConfig.sh fabric-samples/test-network` 54 | 1. `npm start` 55 | 56 | --- 57 | 58 | ## Quick_Start 59 | 60 | 1. Access localhost:3000, click ![TestNetworkSample](doc/quick_sample.png) 61 | 1. Click![submit](doc/quicksample2.png) 62 | 1. Access `localhost:3000/result/BatchTimeout` to see TPS relationship with BatchTimeout 63 | 1. Access `localhost:3000/result/MaxMessageCount` to see TPS relationship with MaxMessageCount 64 | 65 | --- 66 | 67 | ## Docker 68 | If you want to try with docker usage, welcome to use 69 | `docker run -d --rm --name Probe -p 3000:3000 -v /var/run/docker.sock:/var/run/docker.sock -v $(pwd):/home/probe \ 70 | 19902439/probe:latest` 71 | it is based on alphine, and which means, you may need to make sure your minifab/fabric sample works instead, as install for `configtxgen` or able to use docker in docker. 72 | 73 | --- 74 | ## Contributing 75 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 76 | 77 | 1. Fork the Project 78 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 79 | 3. Commit your Changes (`git commit -s`) 80 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 81 | 5. Open a Pull Request 82 | 83 | If your want to contribute Probe with new feature, bug fixing please create a new issue, of course with PR is best. 84 | Also welcome for documentatoin, learning course, etc. 85 | For development, please complete `Quick start` above. 86 | For Frontend, please use `npm run build`. 87 | For backend, please develop `--DryRun` as mock for unit test and adding real cases in CI 88 | 89 | --- 90 | ## License 91 | Hyperledger Project source code files are made available under the Apache License, Version 2.0 (Apache-2.0), located in the [LICENSE](LICENSE) file. 92 | 93 | --- 94 | ## Contact 95 | 96 | * [Maintainers](MAINTAINERS.md) 97 | 98 | --- 99 | 100 | ## Why Probe 101 | 102 | As discussed with [TWGC performance work group](https://github.com/Hyperledger-TWGC/fabric-performance-wiki), we found out that different block parameters, networks, chaincode language and chaincode logic having influence final TPS. 103 | 104 | To answer, the best parameter for specific fabric network and fabric chaincode, this project been created. 105 | 106 | ## Probe is not 107 | 108 | - Real time time monitor, for real time tps monitor, pls use Prometheus. (But Probe has demo for it with test network [here](https://www.bilibili.com/video/BV1x54y1x78Z)) 109 | - Auto test framework for Fabric performance, as in probe, we will invoke as byfn or minifab for your network up/down/cleanup. 110 | - Performance test tool for Fabric, for this we using tape. 111 | - GUI for tape, tape focus on once off time performance testing. 112 | 113 | 114 | ### THANKS FOR CHOOSING 115 | 116 | -------------------------------------------------------------------------------- /README_ZH.MD: -------------------------------------------------------------------------------- 1 | # Probe 2 | [![Build Status](https://dev.azure.com/yy19902439/yy19902439/_apis/build/status/SamYuan1990.Probe?branchName=main)](https://dev.azure.com/yy19902439/yy19902439/_build/latest?definitionId=8&branchName=main) 3 | 4 | ## Probe是什么 5 | 6 | ![Probe](doc/Probe.png) 7 | 根据[性能测试白皮书](https://www.hyperledger.org/learn/publications/blockchain-performance-metrics), Probe是一个面向Hyperledger Fabric维护者,用户,研究者的web UI的应用程序,短期而言旨在提供控制被测网络(SUT)并协调压力生成工具于观察工具,来针对性的观察Fabric出块参数对于Fabric网络性能的影响。 8 | 9 | - 提供循环测试控制。 10 | - 提供可视化的结果对比图。 11 | - 提供基于test-network的配置案例。 12 | 13 | 长期而言Probe计划扮演一个协调者的工作 14 | 15 | - Probe将允许您设计shell脚本来调度SUT和测试工具。 16 | - Probe将允许您以自定义的方式使用GUI来可视化性能指标和配置之间的关系。 17 | 18 | ## 可以用Probe做什么 19 | - [如何通过Probe配置调整Fabric参数](doc/HowToConfigFabricParameters_ZH.md) 20 | 21 | ## 为什么使用Probe 22 | Probe是在[TWGC performance work group](https://github.com/Hyperledger-TWGC/fabric-performance-wiki)的交流中中,我们对于“如何配置出块参数来达到最大tps?”这一问题的回答而创立并实现的项目。 23 | 24 | ## Probe不做什么 25 | - 实时监控,如需要,请参考配置Prometheus 26 | - 完整的自动测试框架,对于SUT的部署方式,目前依赖于test-network 27 | - 性能测试工具(LGC),对于性能测试本身,目前依赖于Tape 28 | - Tape的GUI版本 29 | 30 | ## 快速上手 31 | 1. 安装项目 `npm install` 32 | 33 | 1. 安装fabric-sample环境 34 | 35 | `curl -vsS https://raw.githubusercontent.com/hyperledger/fabric/master/scripts/bootstrap.sh | bash` 36 | 37 | **【注】** 38 | 39 | 如果上述curl命令执行失败,这可能是由于部分国家或地区的网络原因导致curl的ssl握手失败。 40 | 在这种情况下,我们建议用户手工下载[bootstrap.sh](https://raw.githubusercontent.com/hyperledger/fabric/master/scripts/bootstrap.sh)脚本和运行。 41 | 42 | 1. 下载tape的dockeer版本 `docker pull ghcr.io/hyperledger-twgc/tape` 43 | 44 | 1. 将参数控制脚本通过以下命令安装 `cp sample/prepareConfig.sh fabric-samples/test-network` 45 | 46 | 1. 启动Probe `npm start` 47 | 48 | 1. 打开`localhost:3000`, 点击![TestNetworkSample](doc/quick_sample.png) 49 | 50 | 1. 点击![submit](doc/quicksample2.png) 51 | 52 | 1. 待完成后访问 `localhost:3000/result/BatchTimeout` 查看TPS与BatchTimeout之间的关系 53 | 54 | 1. 待完成后访问 `localhost:3000/result/MaxMessageCount` 查看TPS与MaxMessageCount之间的关系 55 | 56 | ## 维护和升级计划 57 | version | define| feature 58 | ---|---|--- 59 | 0.0.4 | customer UI enhancement | allow user chose parameters to show on UI 60 | 0.0.5 | support different deploy plan with peers | 4/5 6/3 8/1 61 | 0.0.6 | support different deploy plan with orgs | 2 3 4 62 | 0.0.7 | support different deploy plan with gossip | 1:1 1:2 1:3 63 | 0.0.8 | support different deploy plan with mvcc | 10 20 40 64 | 0.0.9 | support different deploy plan with data size | 10k 512k 1M 65 | 1.0.0 | fix up bugs after 0.0.4 | 66 | 1.1.x | tools supporting | supports caliper 67 | 1.2.x | network starter supporting | supports cello 68 | 69 | ## 如何贡献 70 | 71 | 如果您想为 Probe 添加新的特性,或者修复 bug,非常欢迎您在创建 issue,当然,如果能提交 PR 会更好。 72 | 73 | 如果您希望贡献文档翻译,或者学习教程,也欢迎和我们联系。 74 | 75 | 对于研发,请先参考`快速上手`完成环境配置。 76 | 77 | 请使用`npm run build`来开发前台。 78 | 79 | 请使用`--DryRun`来通过`mock`的方式开发后台功能,但是请把实际测试案例加入CI。 80 | 81 | ## 学习视频 82 | version | URL | comments 83 | ---|---|--- 84 | 0.0.1 | https://www.bilibili.com/video/BV1x54y1x78Z | N/A 85 | 0.0.1 | https://www.bilibili.com/video/BV1dV411y7oZ | N/A 86 | 0.0.2 | https://www.bilibili.com/video/bv13t4y1B7AE | N/A 87 | 0.0.3 | https://www.bilibili.com/video/BV1Kz4y1179L | N/A 88 | -------------------------------------------------------------------------------- /andLogic.rego: -------------------------------------------------------------------------------- 1 | package tape 2 | 3 | default allow = false 4 | 5 | allow { 6 | input[_] == "org1" 7 | input[_] == "org2" 8 | } -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const createError = require('http-errors'); 2 | const express = require('express'); 3 | const path = require('path'); 4 | const cookieParser = require('cookie-parser'); 5 | const bodyParser = require('body-parser'); 6 | // const logger = require('morgan'); 7 | const ejs = require('ejs'); 8 | 9 | // const indexRouter = require('./routes/index'); 10 | const resultRouter = require('./routes/result'); 11 | const apiRouter = require('./routes/api'); 12 | const quickRouter = require('./routes/quick'); 13 | const newUIRouter = require('./routes/newUI'); 14 | 15 | const app = express(); 16 | const log4js = require('log4js'); 17 | 18 | log4js.configure({ 19 | appenders: {app: {type: 'file', filename: 'logger.log'}}, 20 | categories: {default: {appenders: ['app'], level: 'info'}} 21 | }); 22 | 23 | //const logger = log4js.getLogger(); 24 | //logger.level = "debug"; 25 | 26 | const logger = log4js.getLogger('app'); 27 | // view engine setup 28 | app.set('views', path.join(__dirname, 'views')); 29 | app.engine('html', ejs.__express); 30 | app.set('view engine', 'html'); 31 | 32 | // app.use(logger('dev')); 33 | app.use(express.json()); 34 | app.use(express.urlencoded({extended: false})); 35 | app.use(cookieParser()); 36 | app.use(express.static(path.join(__dirname, 'public'))); 37 | app.use(bodyParser.json()); 38 | app.use(bodyParser.urlencoded({extended: true})); 39 | 40 | // app.use('/oldUI', indexRouter); 41 | app.use('/result', resultRouter); 42 | app.use('/api', apiRouter); 43 | app.use('/quick', quickRouter); 44 | app.use('/', newUIRouter); 45 | 46 | 47 | // catch 404 and forward to error handler 48 | app.use(function(req, res, next) { 49 | next(createError(404)); 50 | }); 51 | 52 | // error handler 53 | app.use(function(err, req, res, next) { 54 | // set locals, only providing error in development 55 | res.locals.message = err.message; 56 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 57 | // render the error page 58 | console.log(err); 59 | res.status(err.status || 500); 60 | res.render(err); 61 | }); 62 | 63 | module.exports = app; 64 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | const app = require('../app'); 8 | const debug = require('debug')('myapp:server'); 9 | const http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | const port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | const server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | const port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | const bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | const addr = server.address(); 86 | const bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | # Definition of nodes 2 | peer1: &peer1 3 | addr: peer0.org1.example.com:7051 4 | tls_ca_cert: /config/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp/tlscacerts/tlsca.org1.example.com-cert.pem 5 | 6 | peer2: &peer2 7 | addr: peer0.org2.example.com:9051 8 | tls_ca_cert: /config/fabric-samples/test-network/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/msp/tlscacerts/tlsca.org2.example.com-cert.pem 9 | 10 | orderer1: &orderer1 11 | addr: orderer.example.com:7050 12 | tls_ca_cert: /config/fabric-samples/test-network/organizations/ordererOrganizations/example.com/msp/tlscacerts/tlsca.example.com-cert.pem 13 | 14 | # Nodes to interact with 15 | endorsers: 16 | - *peer1 17 | - *peer2 18 | # we might support multi-committer in the future for more complex test scenario, 19 | # i.e. consider tx committed only if it's done on >50% of nodes. But for now, 20 | # it seems sufficient to support single committer. 21 | committers: 22 | - *peer2 23 | 24 | commitThreshold: 1 25 | 26 | orderer: *orderer1 27 | 28 | # Invocation configs 29 | channel: mychannel 30 | chaincode: basic 31 | args: 32 | - GetAllAssets 33 | mspid: Org1MSP 34 | private_key: /config/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/keystore/priv_sk 35 | sign_cert: /config/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/signcerts/User1@org1.example.com-cert.pem 36 | num_of_conn: 40 37 | client_per_conn: 40 38 | -------------------------------------------------------------------------------- /data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamYuan1990/Probe/6212c824207ab9ca04b83ca39fc7aeeed47859c2/data/.gitkeep -------------------------------------------------------------------------------- /data/rs.csv: -------------------------------------------------------------------------------- 1 | Chaincode,BatchTimeout,MaxMessageCount,AbsoluteMaxBytes,PreferredMaxBytes,TPS, 2 | sample,1,10,2,512, 740.862453, 3 | sample,1,10,4,512, 123.862453, -------------------------------------------------------------------------------- /doc/HowToConfigFabricParameters.md: -------------------------------------------------------------------------------- 1 | # How to config Fabric block related parameter to get “max” TPS 2 | ## Understanding those paramters 3 | 1st of all, we should know how those 4 parameters working together. 4 | As summary, Fabric seems will detect block match parameters in order of PreferredMaxBytes,AbsoluteMaxBytes,MaxMessageCount and BatchTimeout. 5 | If matched any of them, then the block will be send out. 6 | 7 | ### Avoid using Batch timeout 8 | In this trun, let's use Probe with parameter below to do a testing for Fabric test-network 9 | ``` 10 | BatchTimeout:20 11 | MaxMessageCount:320 12 | AbsoluteMaxBytes:20 13 | PreferredMaxBytes:20480 14 | ``` 15 | Result: 16 | | Chaincode | BatchTimeout | MaxMessageCount | AbsoluteMaxBytes | PreferredMaxBytes | TPS | 17 | |-----------|--------------|-----------------|------------------|-------------------|-------------| 18 | | sample | 20 | 320 | 20 | 20480 | 414\.187180 | 19 | 20 | From the tape log, we can see the last block takes about BatchTimeout duration and down the total tps. 21 | ``` 22 | Time 5.89s Block 33 Tx 320 23 | Time 5.98s Block 34 Tx 320 24 | Time 6.04s Block 35 Tx 320 25 | Time 6.09s Block 36 Tx 320 26 | Time 24.14s Block 37 Tx 80 27 | time="2020-12-11T09:27:48Z" level=info msg="Completed processing transactions." 28 | tx: 10000, duration: 24.143673413s, tps: 414.187180 29 | ``` 30 | There for 31 | # Deduction one:We'd better to avoid orderer sending a new block by a too large Batchtimout. 32 | 33 | ## Relationship among PreferredMaxBytes,AbsoluteMaxBytes,MaxMessageCount 34 | How PreferredMaxBytes,AbsoluteMaxBytes,MaxMessageCount working togther? 35 | In this trun, we config probe as below: 36 | ``` 37 | BatchTimeout:20 38 | MaxMessageCount:10,20,40 39 | AbsoluteMaxBytes:20 40 | PreferredMaxBytes:4 41 | ``` 42 | result: 43 | | Chaincode | BatchTimeout | MaxMessageCount | AbsoluteMaxBytes | PreferredMaxBytes | TPS | 44 | |-----------|--------------|-----------------|------------------|-------------------|-------------| 45 | | sample | 20 | 10 | 20 | 4 | 156\.155258 | 46 | | sample | 20 | 20 | 20 | 4 | 156\.811844 | 47 | | sample | 20 | 40 | 20 | 4 | 157\.936309 | 48 | 49 | From tape log, there alway 1 tx in block as PreferredMaxBytes is 4 kb. 50 | ``` 51 | Time 63.75s Block 10000 Tx 1 52 | Time 63.75s Block 10001 Tx 1 53 | Time 63.76s Block 10002 Tx 1 54 | Time 63.76s Block 10003 Tx 1 55 | Time 63.77s Block 10004 Tx 1 56 | Time 63.77s Block 10005 Tx 1 57 | time="2020-12-11T09:43:16Z" level=info msg="Completed processing transactions." 58 | tx: 10000, duration: 1m3.770693352s, tps: 156.811844 59 | ``` 60 | Suppose PreferredMaxBytes for 4 kb only support for 1 tx in block. 61 | Start a new trun 62 | ``` 63 | BatchTimeout:20 64 | MaxMessageCount:320,640 65 | AbsoluteMaxBytes:20 66 | PreferredMaxBytes:512 67 | ``` 68 | tape log(512k / 135 tx = 3.8k/block) meets the suppose above. 69 | ``` 70 | Time 6.33s Block 67 Tx 135 71 | Time 6.37s Block 68 Tx 135 72 | Time 6.39s Block 69 Tx 135 73 | Time 6.43s Block 70 Tx 135 74 | Time 6.48s Block 71 Tx 135 75 | Time 6.51s Block 72 Tx 135 76 | Time 6.56s Block 73 Tx 135 77 | Time 6.59s Block 74 Tx 135 78 | Time 6.62s Block 75 Tx 135 79 | Time 6.67s Block 76 Tx 135 80 | Time 6.70s Block 77 Tx 135 81 | Time 6.75s Block 78 Tx 135 82 | Time 6.78s Block 79 Tx 135 83 | ``` 84 | 85 | # Deduction Two: With constant tx size(kb), how many txs inside a block can be see as min value among PreferredMaxBytes/tx size,MaxMessageCount, AbsoluteMaxBytes/tx size 86 | 87 | ## Fabric Max TPS value? 88 | ### Hardware 89 | For any Fabric network building on hardware, we can't over hardware limiation, such as kb/s from disk io/network device io. 90 | 91 | ### Parameter failure 92 | To get the max tsp, in fact, what we are going to do, is to make those parameters failure. 93 | A short mathematical provement below: 94 | 95 | #### Mathematical 96 | From Deduction two, we can get that 97 | ``` 98 | block size = min(PreferredMaxBytes,MaxMessageCount * tx size, AbsoluteMaxBytes) 99 | ``` 100 | or: 101 | ``` 102 | tx size = min(PreferredMaxBytes / tx size,MaxMessageCount, AbsoluteMaxBytes / tx size) 103 | ``` 104 | # Deduction three: Fabric TPS,can show as kb/s or tx/s: 105 | ``` 106 | tps(kb/s) = min(PreferredMaxBytes,MaxMessageCount * tx size, AbsoluteMaxBytes)/batchtimout 107 | ``` 108 | or 109 | ``` 110 | tps = min(PreferredMaxBytes / tx size,MaxMessageCount, AbsoluteMaxBytes / tx size)/batchtimout 111 | ``` 112 | 113 | ## How to make it 114 | From business considering, we used to limit BatchTimeout or MaxMessageCount. 115 | Ex: 116 | ### Base on BatchTimeout 117 | At same BatchTimeout,adjust MaxMessageCount,with function 118 | ``` 119 | tps = min(PreferredMaxBytes / tx size,MaxMessageCount, AbsoluteMaxBytes / tx size)/batchtimout 120 | ``` 121 | adjust MaxMessageCount to rich hardware limiation. 122 | here is the config fro probe 123 | ![probe msg config](ProbConfigMsg.png) 124 | result: 125 | | Chaincode | BatchTimeout | MaxMessageCount | AbsoluteMaxBytes | PreferredMaxBytes | TPS | calculation tps | 126 | |-----------|--------------|-----------------|------------------|-------------------|--------------|--------------| 127 | | sample | 0\.5 | 40 | 500 | 512000 | 853\.662773 | 80 | 128 | | sample | 0\.5 | 160 | 500 | 512000 | 1073\.833629 | 320 | 129 | | sample | 0\.5 | 320 | 500 | 512000 | 1168\.482934 | 640 | 130 | | sample | 0\.5 | 640 | 500 | 512000 | 1180\.903770 | 720 | 131 | | sample | 0\.5 | 720 | 500 | 512000 | 1282\.712621 | 1440 | 132 | | sample | 0\.5 | 1280 | 500 | 512000 | 1258\.385389 | 20480 | 133 | | sample | 0\.5 | 2048 | 500 | 512000 | 1258\.623620 | 4096 | 134 | 135 | ![probe msg result](maxMsgCount.png) 136 | ### Base on MaxMessageCount 137 | With same MaxMessageCount,adjust BatchTimeout,base 138 | ``` 139 | tps = min(PreferredMaxBytes / tx size,MaxMessageCount, AbsoluteMaxBytes / tx size)/batchtimout 140 | ``` 141 | 142 | Result: 143 | | Chaincode | BatchTimeout | MaxMessageCount | AbsoluteMaxBytes | PreferredMaxBytes | TPS | 144 | |-----------|--------------|-----------------|------------------|-------------------|--------------| 145 | | sample | 0\.2 | 128000 | 500 | 512000 | 1156\.604634 | 146 | | sample | 0\.5 | 128000 | 500 | 512000 | 1227\.426704 | 147 | | sample | 1 | 128000 | 500 | 512000 | 1291\.886710 | 148 | | sample | 2 | 128000 | 500 | 512000 | 1310\.490079 | 149 | | sample | 5 | 128000 | 500 | 512000 | 1295\.549652 | 150 | | sample | 10 | 128000 | 500 | 512000 | 1184\.683782 | 151 | 152 | ![batch timeout result](batchtimout.png) 153 | #### Appendix 154 | this document basing on[Probe](https://github.com/SamYuan1990/Probe),[Tape](https://github.com/ghcr.io/hyperledger-twgc/tape) and [test-network](https://github.com/hyperledger/fabric-samples),using fabric 2.2 image,on my Max laptop. 155 | -------------------------------------------------------------------------------- /doc/HowToConfigFabricParameters_ZH.md: -------------------------------------------------------------------------------- 1 | # 如何配置Fabric出块参数来达到“最大”TPS 2 | ## 对于参数的了解 3 | 在讨论有关最大tps的问题之前,我们首先要了解Fabric出块的四个参数之间是如何工作的。 4 | 从结论上,Fabric会优先于PreferredMaxBytes,AbsoluteMaxBytes,MaxMessageCount这三个参数,最后是BatchTimeout。 5 | ### 避免落入batch timeout 6 | 我们以如下配置Probe发起对于Fabric的性能测试。 7 | ``` 8 | BatchTimeout:20 9 | MaxMessageCount:320 10 | AbsoluteMaxBytes:20 11 | PreferredMaxBytes:20480 12 | ``` 13 | 得到结果如下: 14 | | Chaincode | BatchTimeout | MaxMessageCount | AbsoluteMaxBytes | PreferredMaxBytes | TPS | 15 | |-----------|--------------|-----------------|------------------|-------------------|-------------| 16 | | sample | 20 | 320 | 20 | 20480 | 414\.187180 | 17 | 18 | 从tape的log中,我们可以发现最后一个块实际上很大程度的影响了fabric的tps。 19 | ``` 20 | Time 5.89s Block 33 Tx 320 21 | Time 5.98s Block 34 Tx 320 22 | Time 6.04s Block 35 Tx 320 23 | Time 6.09s Block 36 Tx 320 24 | Time 24.14s Block 37 Tx 80 25 | time="2020-12-11T09:27:48Z" level=info msg="Completed processing transactions." 26 | tx: 10000, duration: 24.143673413s, tps: 414.187180 27 | ``` 28 | 因此,这个发现表明, 29 | # 推论一:我们最好不要依赖一个过大的batchtimeout值来触发fabric出块。 30 | 31 | ## PreferredMaxBytes,AbsoluteMaxBytes,MaxMessageCount的关系 32 | 那么,PreferredMaxBytes,AbsoluteMaxBytes,MaxMessageCount三个参数之间如何相互影响? 33 | 我们以如下配置Probe发起对于Fabric的性能测试。 34 | ``` 35 | BatchTimeout:20 36 | MaxMessageCount:10,20,40 37 | AbsoluteMaxBytes:20 38 | PreferredMaxBytes:4 39 | ``` 40 | 得到结果如下: 41 | | Chaincode | BatchTimeout | MaxMessageCount | AbsoluteMaxBytes | PreferredMaxBytes | TPS | 42 | |-----------|--------------|-----------------|------------------|-------------------|-------------| 43 | | sample | 20 | 10 | 20 | 4 | 156\.155258 | 44 | | sample | 20 | 20 | 20 | 4 | 156\.811844 | 45 | | sample | 20 | 40 | 20 | 4 | 157\.936309 | 46 | 47 | 从tape的log中,我们可以发现无论如何配置MaxMessageCount,我们的块中永远只有1笔交易。 48 | ``` 49 | Time 63.75s Block 10000 Tx 1 50 | Time 63.75s Block 10001 Tx 1 51 | Time 63.76s Block 10002 Tx 1 52 | Time 63.76s Block 10003 Tx 1 53 | Time 63.77s Block 10004 Tx 1 54 | Time 63.77s Block 10005 Tx 1 55 | time="2020-12-11T09:43:16Z" level=info msg="Completed processing transactions." 56 | tx: 10000, duration: 1m3.770693352s, tps: 156.811844 57 | ``` 58 | 4k大小的PreferredMaxBytes在测试中只能支持一个1tx 59 | 基于我们的测试,一笔交易的大小约为4kb。因此我们调整PreferredMaxBytes并且再次观察。 60 | ``` 61 | BatchTimeout:20 62 | MaxMessageCount:320,640 63 | AbsoluteMaxBytes:20 64 | PreferredMaxBytes:512 65 | ``` 66 | tape log(512k 约等于 135 tx,即3.8k/block) 67 | ``` 68 | Time 6.33s Block 67 Tx 135 69 | Time 6.37s Block 68 Tx 135 70 | Time 6.39s Block 69 Tx 135 71 | Time 6.43s Block 70 Tx 135 72 | Time 6.48s Block 71 Tx 135 73 | Time 6.51s Block 72 Tx 135 74 | Time 6.56s Block 73 Tx 135 75 | Time 6.59s Block 74 Tx 135 76 | Time 6.62s Block 75 Tx 135 77 | Time 6.67s Block 76 Tx 135 78 | Time 6.70s Block 77 Tx 135 79 | Time 6.75s Block 78 Tx 135 80 | Time 6.78s Block 79 Tx 135 81 | ``` 82 | 符合预期,因此我们可以简单的得出一个推论,这三个参数实际上都是限制block中有多少笔交易的参数。 83 | 84 | # 推论二:在tx大小恒定的情况下。(或者我们可以以均值取代等方式)块内交易数目为PreferredMaxBytes/tx size,MaxMessageCount, AbsoluteMaxBytes/tx size中的最小值。 85 | 86 | ## Fabric TPS的上限是什么? 87 | ### 硬件上限 88 | 我们说任何性能的理论上限是整个网络的上限,也就是整组设备中某个设备I/O,或其他的最小值。 89 | 也就是说,无论我们如何优化这四个参数,我们也无法将fabric的tps上限超过网络中某块网卡的最大承载能力。 90 | 91 | ### 参数失效 92 | 因此,为了追求最大的TPS我们需要尽可能的让Fabric某些控制参数失效。来达到这一目的,数学证明如下: 93 | 94 | #### 数学表达 95 | 从上文中我们将PreferredMaxBytes,AbsoluteMaxBytes,MaxMessageCount视为控制一个块内有多少交易。 96 | 即: 97 | ``` 98 | block size = min(PreferredMaxBytes,MaxMessageCount * tx size, AbsoluteMaxBytes) 99 | ``` 100 | 或: 101 | ``` 102 | tx size = min(PreferredMaxBytes / tx size,MaxMessageCount, AbsoluteMaxBytes / tx size) 103 | ``` 104 | 105 | # 推论三: Fabric网络的TPS,可以表达为kb/s或者tx/s,如下: 106 | ``` 107 | tps(kb/s) = min(PreferredMaxBytes,MaxMessageCount * tx size, AbsoluteMaxBytes)/batchtimout 108 | ``` 109 | 或者 110 | ``` 111 | tps = min(PreferredMaxBytes / tx size,MaxMessageCount, AbsoluteMaxBytes / tx size)/batchtimout 112 | ``` 113 | 114 | ## 如何实现 115 | 现实上来说,业务一般会限制BatchTimeout或者MaxMessageCount。或者我们可以从这两个参数入手。 116 | 在如下示例中, 117 | ### 基于BatchTimeout 118 | 在同样的BatchTimeout下,调整MaxMessageCount,基于 119 | ``` 120 | tps = min(PreferredMaxBytes / tx size,MaxMessageCount, AbsoluteMaxBytes / tx size)/batchtimout 121 | ``` 122 | 不断的调整MaxMessageCount直到上限。 123 | 我们以如下 来配置Probe发起对于Fabric的性能测试。 124 | ![probe msg config](ProbConfigMsg.png) 125 | 得到结果如下: 126 | | Chaincode | BatchTimeout | MaxMessageCount | AbsoluteMaxBytes | PreferredMaxBytes | TPS | calculation tps | 127 | |-----------|--------------|-----------------|------------------|-------------------|--------------|--------------| 128 | | sample | 0\.5 | 40 | 500 | 512000 | 853\.662773 | 80 | 129 | | sample | 0\.5 | 160 | 500 | 512000 | 1073\.833629 | 320 | 130 | | sample | 0\.5 | 320 | 500 | 512000 | 1168\.482934 | 640 | 131 | | sample | 0\.5 | 640 | 500 | 512000 | 1180\.903770 | 720 | 132 | | sample | 0\.5 | 720 | 500 | 512000 | 1282\.712621 | 1440 | 133 | | sample | 0\.5 | 1280 | 500 | 512000 | 1258\.385389 | 20480 | 134 | | sample | 0\.5 | 2048 | 500 | 512000 | 1258\.623620 | 4096 | 135 | 136 | ![probe msg result](maxMsgCount.png) 137 | ### 基于MaxMessageCount 138 | 在同样的MaxMessageCount下,调整BatchTimeout,基于 139 | ``` 140 | tps = min(PreferredMaxBytes / tx size,MaxMessageCount, AbsoluteMaxBytes / tx size)/batchtimout 141 | ``` 142 | 不断的调整BatchTimeout直到上限。 143 | 我们以如下 来配置Probe发起对于Fabric的性能测试。 144 | 得到结果如下: 145 | | Chaincode | BatchTimeout | MaxMessageCount | AbsoluteMaxBytes | PreferredMaxBytes | TPS | 146 | |-----------|--------------|-----------------|------------------|-------------------|--------------| 147 | | sample | 0\.2 | 128000 | 500 | 512000 | 1156\.604634 | 148 | | sample | 0\.5 | 128000 | 500 | 512000 | 1227\.426704 | 149 | | sample | 1 | 128000 | 500 | 512000 | 1291\.886710 | 150 | | sample | 2 | 128000 | 500 | 512000 | 1310\.490079 | 151 | | sample | 5 | 128000 | 500 | 512000 | 1295\.549652 | 152 | | sample | 10 | 128000 | 500 | 512000 | 1184\.683782 | 153 | 154 | ![batch timeout result](batchtimout.png) 155 | #### 后记 156 | 本文基于[Probe](https://github.com/SamYuan1990/Probe),[Tape](https://github.com/ghcr.io/hyperledger-twgc/tape)以及[test-network](https://github.com/hyperledger/fabric-samples),基于fabric 2.2版本image,在mac上单机进行的测试。 157 | -------------------------------------------------------------------------------- /doc/ProbConfigMsg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamYuan1990/Probe/6212c824207ab9ca04b83ca39fc7aeeed47859c2/doc/ProbConfigMsg.png -------------------------------------------------------------------------------- /doc/Probe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamYuan1990/Probe/6212c824207ab9ca04b83ca39fc7aeeed47859c2/doc/Probe.png -------------------------------------------------------------------------------- /doc/ProbeUI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamYuan1990/Probe/6212c824207ab9ca04b83ca39fc7aeeed47859c2/doc/ProbeUI.png -------------------------------------------------------------------------------- /doc/batchtimout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamYuan1990/Probe/6212c824207ab9ca04b83ca39fc7aeeed47859c2/doc/batchtimout.png -------------------------------------------------------------------------------- /doc/maxMsgCount.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamYuan1990/Probe/6212c824207ab9ca04b83ca39fc7aeeed47859c2/doc/maxMsgCount.png -------------------------------------------------------------------------------- /doc/quick_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamYuan1990/Probe/6212c824207ab9ca04b83ca39fc7aeeed47859c2/doc/quick_sample.png -------------------------------------------------------------------------------- /doc/quicksample2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamYuan1990/Probe/6212c824207ab9ca04b83ca39fc7aeeed47859c2/doc/quicksample2.png -------------------------------------------------------------------------------- /e2e/minifab.test.js: -------------------------------------------------------------------------------- 1 | const app = require('../app'); 2 | const request = require('supertest')(app); 3 | 4 | describe('# test app.js', function () { 5 | it('Post /api/run/new 200', function (done) { 6 | request 7 | .post('/api/run/new') 8 | .set('Content-Type', 'application/x-www-form-urlencoded') 9 | .send({ 10 | BatchTimeout: '1', 11 | MaxMessageCount: '10,20', 12 | AbsoluteMaxBytes: '103809024', 13 | PreferredMaxBytes: '524288', 14 | path: './minifabric', 15 | cmd: `[ 16 | {"order":0,"cmdType":"Shell","args":["./minifab","up","-i","2.2"]}, 17 | {"order":1,"cmdType":"Shell","args":["./minifab","channelquery","-c","systemchannel"]}, 18 | {"order":2,"cmdType":"Shell","args":["cp","vars/systemchannel_config.json","updatedchannel.json"]}, 19 | {"order":3,"cmdType":"PrePare","args":["./prepareConfig.sh"]}, 20 | {"order":4,"cmdType":"Shell","args":["cp","updatedchannel.json","vars/systemchannel_config.json"]}, 21 | {"order":5,"cmdType":"Shell","args":["./minifab","channelsign,channelupdate","-c","systemchannel"]}, 22 | {"order":6,"cmdType":"Shell","args":["mkdir","-p","/tmp/minifab"]}, 23 | {"order":7,"cmdType":"Shell","args":["cp","config.yaml","/tmp/minifab"]}, 24 | {"order":8,"cmdType":"Shell","args":["cp","-r","./vars","/tmp/minifab"]}, 25 | {"order":9,"cmdType":"Tape","args":["docker","run","--name","tape","-e","TAPE_LOGLEVEL=debug","--network","mysite0","-v","/tmp/minifab:/config","ghcr.io/hyperledger-twgc/tape","tape","-c","/config/config.yaml","-n","500"]}, 26 | {"order":10,"cmdType":"Shell","args":["docker","rm","tape"]}, 27 | {"order":11,"cmdType":"Shell","args":["rm","-rf","/tmp/minifab"]}, 28 | {"order":12,"cmdType":"Shell","args":["sleep","10"]}, 29 | {"order":13,"cmdType":"Shell","args":["./minifab","down"]}, 30 | {"order":14,"cmdType":"Shell","args":["./minifab","cleanup"]}]`, 31 | }) 32 | .expect(200, done); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /e2e/testnetwork.test.js: -------------------------------------------------------------------------------- 1 | const app = require('../app'); 2 | const request = require('supertest')(app); 3 | 4 | describe('# test app.js', function () { 5 | it('Post /api/run/new 200', function (done) { 6 | request 7 | .post('/api/run/new') 8 | .set('Content-Type', 'application/x-www-form-urlencoded') 9 | .send({ 10 | BatchTimeout: '1,2', 11 | MaxMessageCount: '10', 12 | AbsoluteMaxBytes: '2', 13 | PreferredMaxBytes: '512', 14 | path: './fabric-samples/test-network', 15 | cmd: `[ 16 | {"order":0,"cmdType":"PrePare","args":["./prepareConfig.sh"]}, 17 | {"order":1,"cmdType":"Shell","args":["./network.sh","up","createChannel"]}, 18 | {"order":2,"cmdType":"Shell","args":["./network.sh","deployCC","-d","5","-ccn","basic","-ccp","../asset-transfer-basic/chaincode-go/","-ccl","go"]}, 19 | {"order":3,"cmdType":"Shell","args":["sleep","10"]}, 20 | {"order":4,"cmdType":"Tape","args":["docker","run","--name","tape","-e","TAPE_LOGLEVEL=debug","--network","fabric_test","-v",".//:/config","ghcr.io/hyperledger-twgc/tape","tape","-c","/config/config.yaml","-n","500"]}, 21 | {"order":5,"cmdType":"Shell","args":["docker","rm","tape"]}, 22 | {"order":6,"cmdType":"Shell","args":["./network.sh","down"]}, 23 | {"order":7,"cmdType":"Shell","args":["sleep","10"]}]`, 24 | }) 25 | .expect(200, done); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /lib/commands.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | exports.newVersionCommand = function newVersionCommand(cmd, thePath, dryRun, 4 | BatchTimeout, MaxMessageCountElement, AbsoluteMaxBytes, PreferredMaxBytes) { 5 | const command = { 6 | DryRun: false, 7 | tps: false, 8 | }; 9 | if (dryRun) { 10 | command.DryRun = true; 11 | } 12 | command.config = { 13 | cwd : path.resolve(thePath) 14 | }; 15 | command.args = []; 16 | command.command = cmd.args[0]; 17 | if (cmd.cmdType === 'Shell') { 18 | for (let i = 1; i < cmd.args.length; i++) { 19 | command.args[i - 1] = cmd.args[i]; 20 | } 21 | } 22 | if (cmd.cmdType === 'Tape') { 23 | command.tps = true; 24 | command.cmdType = 'Tape'; 25 | let vmount = false; 26 | for (let i = 1; i < cmd.args.length; i++) { 27 | command.args[i - 1] = cmd.args[i]; 28 | if (vmount) { 29 | const arr = cmd.args[i].split(':'); 30 | command.args[i - 1] = path.resolve(arr[0]) + ':' + arr[1]; 31 | vmount = false; 32 | } 33 | if (cmd.args[i] === '-v') { 34 | vmount = true; 35 | } 36 | } 37 | } 38 | if (cmd.cmdType === 'Caliper') { 39 | command.tps = true; 40 | command.cmdType = 'Caliper'; 41 | let vmount = false; 42 | for (let i = 1; i < cmd.args.length; i++) { 43 | command.args[i - 1] = cmd.args[i]; 44 | if (vmount) { 45 | const arr = cmd.args[i].split(':'); 46 | command.args[i - 1] = path.resolve(arr[0]) + ':' + arr[1]; 47 | vmount = false; 48 | } 49 | if (cmd.args[i] === '-v') { 50 | vmount = true; 51 | } 52 | } 53 | } 54 | if (cmd.cmdType === 'PrePare') { 55 | command.args = [BatchTimeout, MaxMessageCountElement, AbsoluteMaxBytes, PreferredMaxBytes]; 56 | } 57 | // console.log(JSON.stringify(command)); 58 | return command; 59 | }; -------------------------------------------------------------------------------- /lib/fileIO.js: -------------------------------------------------------------------------------- 1 | const parse = require('csv-parse/lib/sync'); 2 | const fs = require('fs'); 3 | const log4js = require('log4js'); 4 | const logger = log4js.getLogger('app'); 5 | 6 | exports.BatchTimeout = 'BatchTimeout'; 7 | exports.MaxMessageCount = 'MaxMessageCount'; 8 | exports.AbsoluteMaxBytes = 'AbsoluteMaxBytes'; 9 | exports.PreferredMaxBytes = 'PreferredMaxBytes'; 10 | exports.TPS = 'TPS'; 11 | 12 | function keySort(key) { 13 | return function(a, b) { 14 | return a[key] - b[key]; 15 | }; 16 | } 17 | 18 | exports.loadRs = function loadRs(data, orderby) { 19 | const rs = []; 20 | const input = fs.readFileSync('./data/rs.csv', 'utf-8'); 21 | const records = parse(input, { 22 | columns: true, 23 | skip_empty_lines: true 24 | }); 25 | let orderedRecords = records; 26 | if (orderby) { 27 | orderedRecords = records.sort(keySort(orderby)); 28 | } 29 | orderedRecords.forEach(element => { 30 | rs.push(+element[data]); 31 | }); 32 | return rs; 33 | }; 34 | 35 | 36 | exports.init = function init() { 37 | try { 38 | fs.unlinkSync('./data/rs.csv'); 39 | fs.appendFileSync('./data/rs.csv', 'Chaincode,BatchTimeout,MaxMessageCount,AbsoluteMaxBytes,PreferredMaxBytes,TPS,'); 40 | } catch (err) { 41 | /* 处理错误 */ 42 | logger.error(err); 43 | } 44 | }; 45 | 46 | exports.appendRS = function appendRS(data) { 47 | try { 48 | fs.appendFileSync('./data/rs.csv', '\n' + data); 49 | } catch (err) { 50 | logger.error(err); 51 | } 52 | return 0; 53 | }; 54 | -------------------------------------------------------------------------------- /lib/libs.js: -------------------------------------------------------------------------------- 1 | const process = require('child_process'); 2 | const libcommands = require('./commands'); 3 | const fileIO = require('./fileIO'); 4 | const log4js = require('log4js'); 5 | const logger = log4js.getLogger('app'); 6 | 7 | exports.handleStatus = function handleStatus(rs) { 8 | if (rs.status !== 0) { 9 | if (rs.stderr) { 10 | logger.error(rs.stderr.toString('utf-8')); 11 | // console.log(rs.stderr.toString('utf-8')); 12 | } 13 | if (rs.output) { 14 | logger.error(rs.output.toString('utf-8')); 15 | // console.log(rs.output.toString('utf-8')); 16 | } 17 | if (!rs.stderr && !rs.output) { 18 | logger.error(JSON.stringify(rs)); 19 | } 20 | return -1; 21 | } 22 | return 0; 23 | }; 24 | 25 | exports.executeCommand = function executeCommand(command) { 26 | logger.info(command.info); 27 | if (command.DryRun) { 28 | logger.info(command); 29 | return 0; 30 | } 31 | const rs = process.spawnSync(command.command, command.args, command.config); 32 | if (command.tps) { 33 | logger.info('debugging'); 34 | logger.info(command); 35 | logger.info('debugging'); 36 | if (rs.status !== 0) { 37 | return this.handleStatus(rs); 38 | } 39 | if (command.cmdType === 'Tape') { 40 | return this.TapeTpsFilter(rs); 41 | } 42 | if (command.cmdType === 'Caliper') { 43 | return this.CaliperTpsFilter(rs); 44 | } 45 | } else { 46 | return this.handleStatus(rs); 47 | } 48 | }; 49 | 50 | exports.TapeTpsFilter = function TapeTpsFilter(rs) { 51 | logger.info(rs.output.toString('utf-8')); 52 | let str = rs.output.toString('utf-8', 0); 53 | str = str.substring(str.indexOf('tps:')); 54 | str = str.substring(0, str.indexOf('\n')); 55 | str = str.substring(4); 56 | return str; 57 | }; 58 | 59 | exports.CaliperTpsFilter = function CaliperTpsFilter(rs) { 60 | logger.info(rs.output.toString('utf-8')); 61 | const str = rs.output.toString('utf-8', 0); 62 | const reg_str = str.match(/\|.*\|/g); 63 | const arr = reg_str.toString().split('|'); 64 | return arr[arr.length - 2]; 65 | }; 66 | 67 | exports.RunNew = function RunNew(BatchTimeout, MaxMessageCount, AbsoluteMaxBytes, PreferredMaxBytes, dryRun, cmdPath, cmds) { 68 | let status = 0; 69 | logger.info(dryRun); 70 | logger.info(cmdPath); 71 | BatchTimeout.forEach(BatchTimeoutElement => { 72 | MaxMessageCount.forEach(MaxMessageCountElement => { 73 | AbsoluteMaxBytes.forEach(AbsoluteMaxBytesElement => { 74 | PreferredMaxBytes.forEach(PreferredMaxBytesElement => { 75 | cmds.forEach(cmd => { 76 | logger.info(cmd); 77 | logger.info(BatchTimeoutElement); 78 | logger.info(MaxMessageCountElement); 79 | logger.info(AbsoluteMaxBytesElement); 80 | logger.info(PreferredMaxBytesElement); 81 | const command = libcommands.newVersionCommand(cmd, cmdPath, dryRun, 82 | BatchTimeoutElement, MaxMessageCountElement, AbsoluteMaxBytesElement, PreferredMaxBytesElement); 83 | // console.log(command); 84 | if (!command.tps) { 85 | status = this.executeCommand(command); 86 | if (status !== 0) { 87 | return status; 88 | } 89 | } else { 90 | const TPS = this.executeCommand(command); 91 | if (TPS < 0) { 92 | logger.error('error in tps testing'); 93 | return TPS; 94 | } else { 95 | fileIO.appendRS('sample' + ',' + 96 | BatchTimeoutElement + ',' + 97 | MaxMessageCountElement + ',' + 98 | AbsoluteMaxBytesElement + ',' + 99 | PreferredMaxBytesElement + ',' + 100 | TPS + ','); 101 | } 102 | } 103 | }); 104 | }); 105 | }); 106 | }); 107 | }); 108 | return status; 109 | }; -------------------------------------------------------------------------------- /monitor/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | # Copyright IBM Corp. All Rights Reserved. 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | version: '2' 7 | 8 | networks: 9 | monitor: 10 | 11 | services: 12 | 13 | cadvisor: 14 | container_name: cadvisor 15 | image: google/cadvisor 16 | ports: 17 | - 8080:8080 18 | volumes: 19 | - /:/rootfs:ro 20 | - /var/run:/var/run:ro 21 | - /sys:/sys:ro 22 | - /var/lib/docker/:/var/lib/docker:ro 23 | - /dev/disk/:/dev/disk:ro 24 | networks: 25 | - monitor 26 | 27 | prometheus: 28 | container_name: prometheus 29 | image: prom/prometheus 30 | ports: 31 | - "9090:9090" 32 | volumes: 33 | - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml 34 | networks: 35 | - monitor 36 | 37 | grafana: 38 | container_name: grafana 39 | image: grafana/grafana:5.0.0 40 | ports: 41 | - "3001:3000" 42 | volumes: 43 | - ./prometheus/grafana/grafana:/var/lib/grafana 44 | - ./prometheus/grafana/conf:/usr/share/grafana/conf 45 | networks: 46 | - monitor 47 | 48 | pushGateway: 49 | image: prom/pushgateway 50 | container_name: pushGateway 51 | ports: 52 | - "9091:9091" 53 | networks: 54 | - monitor 55 | 56 | nodeexporter: 57 | container_name: nodeexporter 58 | image: prom/node-exporter 59 | ports: 60 | - 9100:9100 61 | volumes: 62 | - /proc:/host/proc 63 | - /sys:/host/sys 64 | - /:/rootfs 65 | networks: 66 | - monitor -------------------------------------------------------------------------------- /monitor/prometheus/grafana/conf/defaults.ini: -------------------------------------------------------------------------------- 1 | ##################### Grafana Configuration Defaults ##################### 2 | # 3 | # Do not modify this file in grafana installs 4 | # 5 | 6 | # possible values : production, development 7 | app_mode = production 8 | 9 | # instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty 10 | instance_name = ${HOSTNAME} 11 | 12 | #################################### Paths ############################### 13 | [paths] 14 | # Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used) 15 | data = data 16 | 17 | # Temporary files in `data` directory older than given duration will be removed 18 | temp_data_lifetime = 24h 19 | 20 | # Directory where grafana can store logs 21 | logs = data/log 22 | 23 | # Directory where grafana will automatically scan and look for plugins 24 | plugins = data/plugins 25 | 26 | # folder that contains provisioning config files that grafana will apply on startup and while running. 27 | provisioning = conf/provisioning 28 | 29 | #################################### Server ############################## 30 | [server] 31 | # Protocol (http, https, h2, socket) 32 | protocol = http 33 | 34 | # The ip address to bind to, empty will bind to all interfaces 35 | http_addr = 36 | 37 | # The http port to use 38 | http_port = 3000 39 | 40 | # The public facing domain name used to access grafana from a browser 41 | domain = localhost 42 | 43 | # Redirect to correct domain if host header does not match domain 44 | # Prevents DNS rebinding attacks 45 | enforce_domain = false 46 | 47 | # The full public facing url 48 | root_url = %(protocol)s://%(domain)s:%(http_port)s/ 49 | 50 | # Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons. 51 | serve_from_sub_path = false 52 | 53 | # Log web requests 54 | router_logging = false 55 | 56 | # the path relative working path 57 | static_root_path = public 58 | 59 | # enable gzip 60 | enable_gzip = false 61 | 62 | # https certs & key file 63 | cert_file = 64 | cert_key = 65 | 66 | # Unix socket path 67 | socket = /tmp/grafana.sock 68 | 69 | #################################### Database ############################ 70 | [database] 71 | # You can configure the database connection by specifying type, host, name, user and password 72 | # as separate properties or as on string using the url property. 73 | 74 | # Either "mysql", "postgres" or "sqlite3", it's your choice 75 | type = sqlite3 76 | host = 127.0.0.1:3306 77 | name = grafana 78 | user = root 79 | # If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;""" 80 | password = 81 | # Use either URL or the previous fields to configure the database 82 | # Example: mysql://user:secret@host:port/database 83 | url = 84 | 85 | # Max idle conn setting default is 2 86 | max_idle_conn = 2 87 | 88 | # Max conn setting default is 0 (mean not set) 89 | max_open_conn = 90 | 91 | # Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours) 92 | conn_max_lifetime = 14400 93 | 94 | # Set to true to log the sql calls and execution times. 95 | log_queries = 96 | 97 | # For "postgres", use either "disable", "require" or "verify-full" 98 | # For "mysql", use either "true", "false", or "skip-verify". 99 | ssl_mode = disable 100 | 101 | ca_cert_path = 102 | client_key_path = 103 | client_cert_path = 104 | server_cert_name = 105 | 106 | # For "sqlite3" only, path relative to data_path setting 107 | path = grafana.db 108 | 109 | # For "sqlite3" only. cache mode setting used for connecting to the database 110 | cache_mode = private 111 | 112 | #################################### Cache server ############################# 113 | [remote_cache] 114 | # Either "redis", "memcached" or "database" default is "database" 115 | type = database 116 | 117 | # cache connectionstring options 118 | # database: will use Grafana primary database. 119 | # redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=0,ssl=false`. Only addr is required. ssl may be 'true', 'false', or 'insecure'. 120 | # memcache: 127.0.0.1:11211 121 | connstr = 122 | 123 | #################################### Data proxy ########################### 124 | [dataproxy] 125 | 126 | # This enables data proxy logging, default is false 127 | logging = false 128 | 129 | # How long the data proxy should wait before timing out default is 30 (seconds) 130 | timeout = 30 131 | 132 | # If enabled and user is not anonymous, data proxy will add X-Grafana-User header with username into the request, default is false. 133 | send_user_header = false 134 | 135 | #################################### Analytics ########################### 136 | [analytics] 137 | # Server reporting, sends usage counters to stats.grafana.org every 24 hours. 138 | # No ip addresses are being tracked, only simple counters to track 139 | # running instances, dashboard and error counts. It is very helpful to us. 140 | # Change this option to false to disable reporting. 141 | reporting_enabled = true 142 | 143 | # Set to false to disable all checks to https://grafana.com 144 | # for new versions (grafana itself and plugins), check is used 145 | # in some UI views to notify that grafana or plugin update exists 146 | # This option does not cause any auto updates, nor send any information 147 | # only a GET request to https://grafana.com to get latest versions 148 | check_for_updates = true 149 | 150 | # Google Analytics universal tracking code, only enabled if you specify an id here 151 | google_analytics_ua_id = 152 | 153 | # Google Tag Manager ID, only enabled if you specify an id here 154 | google_tag_manager_id = 155 | 156 | #################################### Security ############################ 157 | [security] 158 | # disable creation of admin user on first start of grafana 159 | disable_initial_admin_creation = false 160 | 161 | # default admin user, created on startup 162 | admin_user = admin 163 | 164 | # default admin password, can be changed before first start of grafana, or in profile settings 165 | admin_password = admin 166 | 167 | # used for signing 168 | secret_key = SW2YcwTIb9zpOOhoPsMm 169 | 170 | # disable gravatar profile images 171 | disable_gravatar = false 172 | 173 | # data source proxy whitelist (ip_or_domain:port separated by spaces) 174 | data_source_proxy_whitelist = 175 | 176 | # disable protection against brute force login attempts 177 | disable_brute_force_login_protection = false 178 | 179 | # set to true if you host Grafana behind HTTPS. default is false. 180 | cookie_secure = false 181 | 182 | # set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict", "none" and "disabled" 183 | cookie_samesite = lax 184 | 185 | # set to true if you want to allow browsers to render Grafana in a ,