diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index bab99442..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "env": { - "browser": true, - "commonjs": true, - "es2021": true, - "jest/globals": true - }, - "extends": [ - "standard" - ], - - "parserOptions": { - "ecmaVersion": 2021 - }, - "plugins": ["jest"], - "rules": { - "no-template-curly-in-string": "off", - "no-useless-escape": "off" - } -} diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 30fe89bf..87cbf2cc 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,4 +1,4 @@ # These are supported funding model platforms -custom: ['https://qryn.cloud'] -github: [qxip] +custom: ['https://gigapipe.com'] +github: [qxip, gigapipehq] diff --git a/.github/actions/get-view/action.yml b/.github/actions/get-view/action.yml index 4af5a7a2..19298f61 100644 --- a/.github/actions/get-view/action.yml +++ b/.github/actions/get-view/action.yml @@ -3,7 +3,6 @@ runs: using: "composite" steps: - run: | - pwd && mkdir -p view && cd view && \ wget https://github.com/metrico/qryn-view/releases/download/$TAG/dist.zip && \ - unzip -o dist.zip && rm dist.zip + unzip -d view/dist -o dist.zip && rm dist.zip shell: bash diff --git a/.github/workflows/build_release.yml b/.github/workflows/build_release.yml index f943b763..d2b76cec 100644 --- a/.github/workflows/build_release.yml +++ b/.github/workflows/build_release.yml @@ -1,201 +1,58 @@ -name: 'CI+CD' +name: 'CI+CD Gigapipe' on: - release: - types: [published] workflow_dispatch: inputs: - TAG_NAME: - description: 'Release Version Tag (0.0.0)' + ref_name: + description: Release Version + required: true + branch: + description: Release Branch + type: string + default: 'main' required: true + release: + required: true + type: boolean + release: + types: [created] jobs: - build: - name: 'Build & Publish' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4.1.1 - with: - ref: ${{ github.ref }} - - name: Update Package to Release version - uses: technote-space/package-version-check-action@v1.9.3 - - name: Check NPM secret presence - id: checksecrets - shell: bash - run: | - if [ "$SECRET" == "" ]; then - echo "secretspresent=false" >> $GITHUB_OUTPUT - else - echo "secretspresent=true" >> $GITHUB_OUTPUT - fi - env: - SECRET: ${{ secrets.NPM_TOKEN }} - - uses: actions/setup-node@v4.0.1 - if: ${{ steps.checksecrets.outputs.secretspresent }} - with: - node-version: 18 - - name: build qryn-view - uses: ./.github/actions/get-view - env: - TAG: v3.3.2 - - name: Publish to NPM - if: ${{ steps.checksecrets.outputs.secretspresent }} - continue-on-error: true - run: | - npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN} - npm install - npm publish --access public - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - node: - name: 'Build & Publish Node' - runs-on: ubuntu-latest - needs: build + strategy: + matrix: + os: [ubuntu-latest] + runs-on: ${{ matrix.os }} + name: 'Build & Publish' steps: - uses: actions/checkout@v4.1.1 with: - ref: ${{ github.ref }} - - name: Update Package to Release version - uses: technote-space/package-version-check-action@v1.9.3 - - name: Check Docker secret presence - id: checkdocker - shell: bash - run: | - if [ "$SECRET" == "" ]; then - echo "secretspresent=false" >> $GITHUB_OUTPUT - else - echo "secretspresent=true" >> $GITHUB_OUTPUT - fi - env: - SECRET: ${{ secrets.DOCKERHUB_TOKEN }} + ref: ${{ (github.event_name == 'workflow_dispatch') && github.event.inputs.branch || github.ref }} - name: build qryn-view uses: ./.github/actions/get-view env: TAG: v3.3.2 - - name: Set up Docker QEMU - if: ${{ steps.checkdocker.outputs.secretspresent }} - uses: docker/setup-qemu-action@v3.0.0 - with: - platforms: amd64, arm64 - - name: Set up Docker Buildx - if: ${{ steps.checkdocker.outputs.secretspresent }} - uses: docker/setup-buildx-action@v3.0.0 - - - name: Login to DockerHub - if: ${{ steps.checkdocker.outputs.secretspresent }} - uses: docker/login-action@v3.0.0 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push to Docker Hub - if: ${{ steps.checkdocker.outputs.secretspresent }} - uses: docker/build-push-action@v6 - with: - context: "." - platforms: linux/amd64, linux/arm64 - push: true - tags: | - qxip/qryn:latest - qxip/qryn:${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }} - qxip/cloki:latest - qxip/cloki:${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }} - labels: | - dev.qryn.image.title=qryn - dev.qryn.image.type=nodejs - dev.qryn.image.version={{github.event.inputs.TAG_NAME || github.event.release.tag_name }} + - run: bash -c 'pwd && ls view/dist' - name: Log in to the GHCR registry uses: docker/login-action@v3.0.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build and push to GHCR uses: docker/build-push-action@v6 with: - platforms: linux/amd64, linux/arm64 context: "." + build-args: | + VIEW=1 file: ./Dockerfile push: true + platforms: linux/amd64,linux/arm64,linux/arm/v7 tags: | - ghcr.io/metrico/qryn:latest - ghcr.io/metrico/qryn:${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }} - labels: | - dev.qryn.image.title=qryn - dev.qryn.image.type=nodejs - dev.qryn.image.version={{github.event.inputs.TAG_NAME || github.event.release.tag_name }} - - bun: - name: 'Build & Publish Bun' - runs-on: ubuntu-latest - needs: build - steps: - - uses: actions/checkout@v4.1.1 - with: - ref: ${{ github.ref }} - - name: Update Package to Release version - uses: technote-space/package-version-check-action@v1.9.3 - - name: Check Docker secret presence - id: checkdocker - shell: bash - run: | - if [ "$SECRET" == "" ]; then - echo "secretspresent=false" >> $GITHUB_OUTPUT - else - echo "secretspresent=true" >> $GITHUB_OUTPUT - fi - env: - SECRET: ${{ secrets.DOCKERHUB_TOKEN }} - - name: build qryn-view - uses: ./.github/actions/get-view - env: - TAG: v3.3.2 - - name: Set up Docker QEMU - if: ${{ steps.checkdocker.outputs.secretspresent }} - uses: docker/setup-qemu-action@v3.0.0 - with: - platforms: amd64, arm64 - - name: Set up Docker Buildx - if: ${{ steps.checkdocker.outputs.secretspresent }} - uses: docker/setup-buildx-action@v3.0.0 - - name: Login to DockerHub - if: ${{ steps.checkdocker.outputs.secretspresent }} - uses: docker/login-action@v3.0.0 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push to Docker Hub (bun) - if: ${{ steps.checkdocker.outputs.secretspresent }} - uses: docker/build-push-action@v6 - with: - context: "." - platforms: linux/amd64, linux/arm64 - file: ./Dockerfile_bun - push: true - tags: | - qxip/qryn:bun - qxip/qryn:${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }}-bun - labels: | - dev.qryn.image.title=qryn - dev.qryn.image.type=bun - dev.qryn.image.version={{github.event.inputs.TAG_NAME || github.event.release.tag_name }} - - name: Log in to the GHCR registry - uses: docker/login-action@v3.0.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push to GHCR (bun) - uses: docker/build-push-action@v6 - with: - context: "." - platforms: linux/amd64, linux/arm64 - file: ./Dockerfile_bun - push: true - tags: | - ghcr.io/metrico/qryn:bun - ghcr.io/metrico/qryn:${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }}-bun + ghcr.io/metrico/gigapipe:latest + ghcr.io/metrico/gigapipe:${{ (github.event_name == 'workflow_dispatch') && github.event.inputs.ref_name || github.ref_name }} labels: | - dev.qryn.image.title=qryn - dev.qryn.image.type=bun - dev.qryn.image.version={{github.event.inputs.TAG_NAME || github.event.release.tag_name }} + dev.qryn.image.title=gigapipe diff --git a/.github/workflows/bun-clickhouse.js.yml b/.github/workflows/bun-clickhouse.js.yml deleted file mode 100644 index 5bcca0c0..00000000 --- a/.github/workflows/bun-clickhouse.js.yml +++ /dev/null @@ -1,44 +0,0 @@ -# This workflow will validate qryn using bun + clickhouse - -name: QRYN BUN CI - -on: - push: - branches: [ master, beta ] - paths-ignore: - - '**.md' - - '**.yml' - - '**.yaml' - pull_request: - branches: [ master, beta ] - paths-ignore: - - '**.md' - - '**.yml' - - '**.yaml' - workflow_dispatch: - inputs: - clickhouse_tag: - description: 'Tag for ClickHouse (23.8-alpine)' - required: false - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - bun-version: [latest] - services: - clickhouse: - image: "clickhouse/clickhouse-server:${{ github.event.inputs.clickhouse_tag || '23.8-alpine' }}" - ports: - - 8123:8123 - - 9000:9000 - steps: - - uses: actions/checkout@v4 - - run: rm -rf package-lock.json - - run: git submodule init - - run: git submodule update - - name: Workflow Telemetry - uses: runforesight/workflow-telemetry-action@v1.8.7 - if: github.event_name != 'pull_request' - - run: 'sh test/e2e/compose/clickhouse_sso_e2e_bun/run_test.sh' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 8672d788..a0386bc8 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,3 +1,4 @@ +# TODO: Check if it works for new version # For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # @@ -36,18 +37,18 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'javascript' ] + language: [ 'go', 'javascript' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -58,7 +59,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -72,4 +73,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/k6_test.yml b/.github/workflows/k6_test.yml index 46208c51..d9abeb9a 100644 --- a/.github/workflows/k6_test.yml +++ b/.github/workflows/k6_test.yml @@ -1,3 +1,4 @@ +# TODO! Change the way qryn runs name: K6 Test qxip/qryn on: push: @@ -117,7 +118,7 @@ jobs: run: | sed -i 's/[^[:print:]]/*/' *_summary.txt sed -i 's/[\d128-\d255]//g' *_summary.txt - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4.6.2 with: name: results path: | diff --git a/.github/workflows/manual_ci.yml b/.github/workflows/manual_ci.yml deleted file mode 100644 index 11e2bd75..00000000 --- a/.github/workflows/manual_ci.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: 'Manual Bun CI+CD' - -on: - workflow_dispatch: - inputs: - TAG_NAME: - description: 'Release Version Tag (0.0.0)' - required: true - -jobs: - bun: - name: 'Build & Publish Bun' - runs-on: ubuntu-latest - steps: - - uses: 'actions/checkout@v4.1.1' - with: - ref: ${{ github.ref }} - - name: Check Docker secret presence - id: checkdocker - shell: bash - run: | - if [ "$SECRET" == "" ]; then - echo "secretspresent=false" >> $GITHUB_OUTPUT - else - echo "secretspresent=true" >> $GITHUB_OUTPUT - fi - env: - SECRET: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Set up Docker QEMU - if: ${{ steps.checkdocker.outputs.secretspresent }} - uses: docker/setup-qemu-action@v3.0.0 - with: - platforms: amd64, arm64 - - name: Set up Docker Buildx - if: ${{ steps.checkdocker.outputs.secretspresent }} - uses: docker/setup-buildx-action@v3.0.0 - - name: Login to DockerHub - if: ${{ steps.checkdocker.outputs.secretspresent }} - uses: docker/login-action@v3.0.0 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push to Docker Hub (bun) - if: ${{ steps.checkdocker.outputs.secretspresent }} - uses: docker/build-push-action@v5.0.0 - with: - platforms: linux/amd64, linux/arm64 - file: ./Dockerfile_bun - push: true - tags: | - qxip/qryn:bun - - name: Log in to the GHCR registry - uses: docker/login-action@v2.0.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push to GHCR (bun) - uses: docker/build-push-action@v3.0.0 - with: - platforms: linux/amd64, linux/arm64 - file: ./Dockerfile_bun - push: true - tags: | - ghcr.io/metrico/qryn:bun diff --git a/.github/workflows/node-clickhouse-cluster.js.yml b/.github/workflows/node-clickhouse-cluster.js.yml index 00200bd8..8d6376b4 100644 --- a/.github/workflows/node-clickhouse-cluster.js.yml +++ b/.github/workflows/node-clickhouse-cluster.js.yml @@ -4,36 +4,28 @@ name: QRYN CI CLUSTER on: push: - branches: [ master, beta ] + branches: [ master, gigapipe ] paths-ignore: - '**.md' - '**.yml' - '**.yaml' pull_request: - branches: [ master, beta ] + branches: [ master, gigapipe ] paths-ignore: - '**.md' - '**.yml' - '**.yaml' - workflow_dispatch: - inputs: - clickhouse_tag: - description: 'Tag for ClickHouse (23.8-alpine)' - required: false jobs: build: runs-on: ubuntu-latest - strategy: - matrix: - node-version: [18, 16, 20] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - uses: actions/checkout@v4 - - run: npm install - - run: npm run postinstall - - run: git submodule init - - run: git submodule update + - name: Checkout qryn-test repository + uses: actions/checkout@v4 + with: + repository: metrico/qryn-test + path: ./deps/qryn-test - name: Install Compose uses: ndeloof/install-compose-action@v0.0.1 with: @@ -42,6 +34,9 @@ jobs: - name: Workflow Telemetry uses: runforesight/workflow-telemetry-action@v1.8.7 if: github.event_name != 'pull_request' - - env: - NODE_VERSION: ${{ matrix.node-version }} - run: "sh ./test/e2e/compose/clickhouse_cluster_e2e/run_test.sh" + - id: execution + continue-on-error: true + run: "make e2e-ci" + - name: Rollback Deployment + if: failure() && steps.execution.outcome == 'failure' + run: npm test -- -u diff --git a/.github/workflows/node-clickhouse.js.yml b/.github/workflows/node-clickhouse.js.yml deleted file mode 100644 index df94db04..00000000 --- a/.github/workflows/node-clickhouse.js.yml +++ /dev/null @@ -1,52 +0,0 @@ -# This workflow will validate qryn using nodejs + clickhouse - -name: QRYN CI - -on: - push: - branches: [ master, beta ] - paths-ignore: - - '**.md' - - '**.yml' - - '**.yaml' - pull_request: - branches: [ master, beta ] - paths-ignore: - - '**.md' - - '**.yml' - - '**.yaml' - workflow_dispatch: - inputs: - clickhouse_tag: - description: 'Tag for ClickHouse (23.8-alpine)' - required: false - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [18, 20, 22] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - services: - clickhouse: - image: "clickhouse/clickhouse-server:${{ github.event.inputs.clickhouse_tag || '23.8-alpine' }}" - ports: - - 8123:8123 - - 9000:9000 - steps: - - uses: actions/checkout@v4 - - name: build qryn-view - uses: ./.github/actions/get-view - env: - TAG: v3.3.2 - - name: check if qryn-view pulled - run: pushd view && ls | grep assets && popd - - run: git submodule init - - run: git submodule update - - name: Workflow Telemetry - uses: runforesight/workflow-telemetry-action@v1.8.7 - if: github.event_name != 'pull_request' - - env: - NODE_VERSION: ${{ matrix.node-version }} - run: "sh ./test/e2e/compose/clickhouse_sso_e2e/run_test.sh" diff --git a/.github/workflows/resync.yml b/.github/workflows/resync.yml index c9bc0036..bf88b237 100644 --- a/.github/workflows/resync.yml +++ b/.github/workflows/resync.yml @@ -1,3 +1,4 @@ +# TODO: Check if it works for new version name: Resync PR with Master on: diff --git a/.gitignore b/.gitignore index 5732e78e..4d6aa3db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,3 @@ -node_modules -/docker-env/ -/docker-env/ -/test/e2e/ -/lib/influx/.idea/ -/lib/influx/influx.iml -/wasm_parts/_vendor.zip -/wasm_parts/.idea/ -/wasm_parts/vendor/ -/wasm_parts/main.wasm -/wasm_parts/wasm_parts.iml -/test/qryn_test_env/clickhouse/_data/ -/test/qryn_test_env/grafana/_data/ -/test/qryn_test_cluster_env/grafana/_data/ +/.idea/ +*.idx +/docker diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 5def0990..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "test/e2e"] - path = test/e2e - url = https://github.com/metrico/cloki-test.git diff --git a/CITATION.cff b/CITATION.cff deleted file mode 100644 index c4e0d387..00000000 --- a/CITATION.cff +++ /dev/null @@ -1,18 +0,0 @@ ---- -cff-version: 1.2.0 -version: 3.2.0 -title: "qryn: polyglot, lightweight observability stack, drop-in LGTM alternative" -license: AGPLv3 -abstract: > - All-in-one Polyglot Observability API compatible with Loki, Prometheus, Tempo, Pyroscope, Opentelemetry and more! - Drop-in Grafana LGTM replacement with LogQL, PromQL, TempoQL and Phlare. ClickHouse OLAP Storage. - Ready to use Splunk/Datadog/Elastic alternative! WASM powered -date-released: "2024-04-01" -keywords: - - observability - - opentelemetry - - bun - - wasm -repository-code: "https://github.com/metrico/qryn" -type: software -message: "Available at https://github.com/metrico/qryn" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index a44ff6a7..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,91 +0,0 @@ -# Contributing - -When contributing to this repository, please first discuss the change you wish to make via issue, -email, matrix or any other method with the owners of this repository before making a change. - -Please note we have a code of conduct, please follow it in all your interactions with the project. - -## Pull Request Process - -1. Ensure any install or build dependencies are removed before the end of the layer when doing a - build. -2. Update the README.md and/or any other documentation with details of changes to the interface, - this includes new environment variables, exposed ports, useful file locations and other parameters. -4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you - do not have permission to do that, you may request the second reviewer to merge it for you. - -## Code of Conduct - -### Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. - -### Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or -advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting -* Making demands towards other community members and opensource maintainers. - -### Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -### Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -### Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at support@qryn.dev. All reports and -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -### Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version diff --git a/Dockerfile b/Dockerfile index 74252f82..5bb172f2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,15 @@ -# qryn -FROM node:20.17-slim - -COPY . /app -WORKDIR /app - -RUN groupadd -r qryn && useradd -r -g qryn -m qryn && chown -R qryn:qryn /app -USER qryn - -RUN npm install --omit=dev - -# Expose Ports +FROM golang:1.23.4-alpine as builder +COPY . /src +WORKDIR /src +ARG VIEW +RUN if [ "$VIEW" = "1" ]; then \ + go build -tags view -o gigapipe . ; \ + else \ + go build -o gigapipe . ; \ + fi + +FROM alpine:3.21 +COPY --from=builder /src/gigapipe /gigapipe +ENV PORT 3100 EXPOSE 3100 - -CMD [ "npm", "--offline", "--logs-max=0", "start" ] +CMD /gigapipe diff --git a/Dockerfile_bun b/Dockerfile_bun deleted file mode 100644 index 79dce7e7..00000000 --- a/Dockerfile_bun +++ /dev/null @@ -1,15 +0,0 @@ -# qryn bun builder -FROM oven/bun:latest - -COPY . /app -WORKDIR /app - -RUN groupadd -r qryn && useradd -r -g qryn -m qryn && chown -R qryn:qryn /app -USER qryn - -RUN rm -rf package-lock.json -RUN bun install - -# Expose Ports -EXPOSE 3100 -CMD [ "bun", "--bun", "qryn.mjs" ] diff --git a/LICENSE b/LICENSE index a1018609..0ad25db4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ - GNU AFFERO GENERAL PUBLIC LICENSE + GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..cfc12cd2 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +docker-compose ?= docker-compose + +docker: + docker build -t scripts/deploy/docker/Dockerfile gigapipe . + +e2e-deps: + if [ ! -d ./deps/qryn-test ]; then git clone https://github.com/metrico/qryn-test.git ./deps/qryn-test; fi + cd deps/qryn-test && git pull + +e2e-build: + docker build -f scripts/deploy/docker/Dockerfile -t gigapipe . + +e2e-test: + $(docker-compose) -f ./scripts/test/e2e/docker-compose.yml up -d && \ + docker rm -f qryn-go-test && \ + sleep 60 && \ + docker run \ + -v `pwd`/deps/qryn-test:/deps/e2e \ + --network=e2e_common \ + --name=qryn-go-test \ + -e INTEGRATION_E2E=1\ + -e CLOKI_EXT_URL="e2e.aio:9080" \ + -e QRYN_LOGIN=a \ + -e QRYN_PASSWORD=b \ + -e OTEL_COLL_URL="http://a:b@e2e.aio:9080" \ + node:18-alpine \ + sh -c 'cd /deps/e2e && npm install && npm test -- --forceExit' + +e2e-cleanup: + $(docker-compose) -f ./scripts/test/e2e/docker-compose.yml down + docker rm -f qryn-go-test + +e2e-full: e2e-deps e2e-build e2e-test e2e-cleanup + +e2e-ci: e2e-build e2e-test e2e-cleanup diff --git a/README.md b/README.md index a6495ab8..29962805 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,19 @@ -<a href="https://qryn.dev" target="_blank"> -<img src='https://user-images.githubusercontent.com/1423657/218816262-e0e8d7ad-44d0-4a7d-9497-0d383ed78b83.png' style="margin-left:-10px" width=350/> +<a href="https://gigapipe.com" target="_blank"> +<img src='https://github.com/user-attachments/assets/fc8c7ca9-7a18-403d-b2a6-17899a534d33' style="margin-left:-10px;width:200px;" width=200/> </a> -[](https://github.com/metrico/qryn/actions/workflows/build_release.yml) - - +# <img src='https://avatars.githubusercontent.com/u/76224143?s=400&u=4e207cf756a7146392f9f04c6beb3940a417369d&v=4' style="margin-left:-10px" width=28 /> [gigapipe: polyglot observability](https://gigapipe.com) +> formerly known as _qryn_ <img src="https://user-images.githubusercontent.com/1423657/232089970-c4536f16-5967-4051-85a5-8ad94fcde67c.png" height=50> <img src="https://github.com/metrico/qryn/assets/1423657/546faddb-fbc6-4af5-9e32-4db6da10915d" height=49> -# <img src='https://user-images.githubusercontent.com/1423657/218816262-e0e8d7ad-44d0-4a7d-9497-0d383ed78b83.png' style="margin-left:-10px" width=28 /> [qryn: polyglot observability](https://qryn.dev) +[](https://github.com/metrico/qryn/actions/workflows/build_release.yml) + + -:rocket: _lighweight, multi-standard, polyglot **observability** stack for **Logs, Metrics**, **Traces** and **Profiling**_<br/> -> ... it's pronounced /ˈkwɪr..ɪŋ/ or just _querying_ +:rocket: Gigapipe is a _lighweight, multi-standard, polyglot **observability** stack for **Logs, Metrics**, **Traces** and **Profiling**_<br/> * **Polyglot**: All-in-one, Drop-in compatible with **Loki**, **Prometheus**, **Tempo**, **Pyroscope** * **Lightweight**: Powered by **Bun** - the fast, all-in-one JavaScript runtime + ClickHouse **OLAP** Engine @@ -23,53 +23,29 @@ * **Secure**: Retain total control of data, using **ClickHouse**, **DuckDB** or **InfluxDB** IOx with **S3** object storage * **Indepentent**: Opensource, Community powered, Anti lock-in alternative to Vendor controlled stacks -<!--  --> - +💡 _**gigapipe** independently implements popular observability standards, protocols and query languages_ <br> -## 🚀 [Get Started](https://qryn.metrico.in/#/installation) +# 🚀 Get Started -* Setup & Deploy **qryn** _OSS_ using the [documentation](https://qryn.metrico.in/#/installation) and get help in our [Matrix room](https://matrix.to/#/#qryn:matrix.org) :octocat: -* Looking for a minimal setup for a quick test? Start with [qryn-minimal](https://github.com/metrico/qryn-minimal) - -<a href="https://qryn.cloud" target="_blank"> -<img src="https://github.com/metrico/qryn/assets/1423657/8b93d7cb-442c-4454-b247-27b00ae78384"> -<!-- <img src="https://user-images.githubusercontent.com/1423657/218818279-3efff74f-0191-498a-bdc4-f2650c9d3b49.gif"> --> -</a> - -<br> - -<br> +* Setup & Deploy **gigapipe** _opensource_ using the [documentation](https://gigapipe.com/docs/oss) and get help in our [Matrix room](https://matrix.to/#/#qryn:matrix.org) :octocat: +* Looking for a quick test before installing? Signup for a free trial at [gigapipe.com](https://gigapipe.com) ## Features -💡 _**qryn** independently implements popular observability standards, protocols and query languages_ - -<br> - -### :eye: Built-In Explorer - -**qryn** ships with **view** - our zero dependency, lightweight data explorer for **Logs, Metrics** and **Traces** - -<a href="https://qryn.dev" target="_blank"> -<img src="https://user-images.githubusercontent.com/1423657/200136242-f4133229-ee7c-45e0-8228-8734cf56140a.gif" width=700 class=border /> -</a> - -<br> - -## ➡️ Ingest +## ➡️ Ingestion ### 📚 OpenTelemetry -⚡ **qryn** is officially integrated with [opentelemetry](https://github.com/metrico/otel-collector) supports _any log, trace or metric format_<br> -Ingested data can be queried using any of the avialable qryn APIs _(LogQL, PromQL, TraceQL)_ +⚡ **gigapipe** is officially integrated with [opentelemetry](https://github.com/metrico/otel-collector) supports _any log, trace or metric format_<br> +Ingested data can be queried using any of the avialable gigapipe APIs _(LogQL, PromQL, TraceQL)_ > 💡 _No modifications required to your opentelemetry instrumentation!_ -### 📚 Native -**qryn** supports [native ingestion](https://qryn.metrico.in/#/support) for Loki, Prometheus, Tempo/Zipkin and _[many other protocols](https://qryn.metrico.in/#/support)_<br> -With qryn users can _push data using any combination of supported APIs and formats_ +### 📚 Native APIs +**gigapipe** supports [native ingestion](https://gigapipe.com/docs/api) for Loki, Prometheus, Tempo/Zipkin, Pyroscope and _[many other protocols](https://gigapipe.com/docs/api)_<br> +With gigapipe integrators can _push and read data using any desired combination of APIs and formats_ -> 💡 _No opentelemetry or any other middleware/proxy required!_ +> 💡 _No opentelemetry or any other middlewayre/proxy required!_ <br> @@ -77,13 +53,13 @@ With qryn users can _push data using any combination of supported APIs and forma ### 📚 Loki + LogQL -> Any Loki compatible client or application can be used with qryn out of the box +> Any Loki compatible client or application can be used with gigapipe out of the box -⚡ **qryn** implements the [Loki API](https://github.com/lmangani/qryn/wiki/LogQL-Supported-Queries) for transparent compatibility with **[LogQL](https://grafana.com/docs/loki/latest/query/)** clients<br> +⚡ **gigapipe** implements the *Loki API* for transparent compatibility with **[LogQL](https://grafana.com/docs/loki/latest/query/)** clients<br> The Grafana Loki datasource can be used to natively browse and query _logs_ and display extracted _timeseries_<br> -<a href="https://qryn.metrico.in/#/logs/query" target="_blank"> +<a href="https://gigapipe.com/docs" target="_blank"> <img src="https://user-images.githubusercontent.com/1423657/196654073-b84a218c-6a70-49bb-a477-e8be5714e0ba.gif" width=700 class=border /> </a> @@ -95,13 +71,13 @@ The Grafana Loki datasource can be used to natively browse and query _logs_ and ### 📈 Prometheus + PromQL -> Any Prometheus compatible client or application can be used with qryn out of the box +> Any Prometheus compatible client or application can be used with gigapipe out of the box -⚡ **qryn** implements the [Prometheus API](https://prometheus.io/docs/prometheus/latest/querying/api/) for transparent **[PromQL](https://prometheus.io/docs/prometheus/latest/querying/basics/)** compatibility using WASM 🏆<br> +⚡ **gigapipe** implements the *Prometheus API* for transparent **[PromQL](https://prometheus.io/docs/prometheus/latest/querying/basics/)** compatibility using WASM 🏆<br> The Grafana Prometheus datasource can be used to natively to query _metrics_ and display _timeseries_<br> -<a href="https://qryn.metrico.in/#/metrics/query" target="_blank"> +<a href="https://gigapipe.com/docs" target="_blank"> <img src="https://user-images.githubusercontent.com/1423657/196654084-1f1d8a62-3fd2-4420-a2fa-57ac2872938c.gif" width=700 class=border /> </a> @@ -114,13 +90,13 @@ The Grafana Prometheus datasource can be used to natively to query _metrics_ and ### 🕛 Tempo + TraceQL -⚡ **qryn** implements the [Tempo API](https://github.com/lmangani/qryn/wiki/LogQL-Supported-Queries) for transparent compatibility with **[TraceQL](https://grafana.com/docs/tempo/latest/traceql/)** clients.<br> +⚡ **gigapipe** implements the **Tempo API** for transparent compatibility with **[TraceQL](https://grafana.com/docs/tempo/latest/traceql/)** clients.<br> -> Any Tempo/Opentelemetry compatible client or application can be used with qryn out of the box +> Any Tempo/Opentelemetry compatible client or application can be used with gigapipe out of the box The Tempo datasource can be used to natively query _traces_ including _**TraceQL**_ and supporting _service graphs_<br> -<a href="https://qryn.metrico.in/#/telemetry/query" target="_blank"> +<a href="https://gigapipe.com/docs" target="_blank"> <img src="https://user-images.githubusercontent.com/1423657/196654097-8a235253-bf5d-4937-9e78-fddf12819d44.gif" width=700 class=border /> </a> @@ -131,9 +107,9 @@ The Tempo datasource can be used to natively query _traces_ including _**TraceQL <br> ### 🔥 Pyroscope + Phlare -⚡ **qryn** implements the [Pyroscope/Phlare API](https://qryn.metrico.in/#/profiling/ingestion) for transparent compatibility with **Pyroscope SDK** clients.<br> +⚡ **gigapipe** implements the **Pyroscope API** for transparent compatibility with **Pyroscope SDK** clients.<br> -> Any Pyroscope SDK client or Pyroscope compatible agent can be used with qryn out of the box for **continuous profiling** +> Any Pyroscope SDK client or Pyroscope compatible agent can be used with gigapipe out of the box for **continuous profiling** <img src="https://github.com/metrico/qryn/assets/1423657/0bd11ca9-a2b4-41ee-9ea1-6f21fa4077b2" width=700> @@ -141,14 +117,24 @@ The Tempo datasource can be used to natively query _traces_ including _**TraceQL <br> +### :eye: Built-In Explorer + +**gigapipe** ships with **view** - our zero dependency, lightweight data explorer for **Logs, Metrics** and **Traces** + +<a href="https://gigape.com/docs" target="_blank"> +<img src="https://user-images.githubusercontent.com/1423657/200136242-f4133229-ee7c-45e0-8228-8734cf56140a.gif" width=700 class=border /> +</a> + +<br> + ### 📚 Vendors Compatibility -**qryn** can ingest data using formats from [Grafana, InfluxDB, DataDog, Elastic](https://qryn.metrico.in/#/support) and other vendors. +**gigapipe** can ingest data using formats from [Grafana, InfluxDB, DataDog, Elastic](https://gigapipe.com/docs) and other vendors. <br> -With **qryn** and **grafana** everything _just works_ right out of the box: +With **gigapipe** and **grafana** everything _just works_ right out of the box: - Native datasource support without any plugin or extension - Advanced Correlation between Logs, Metrics and Traces @@ -156,7 +142,7 @@ With **qryn** and **grafana** everything _just works_ right out of the box: <br> -<a href="https://qryn.dev" target="_blank"> +<a href="https://gigape.com" target="_blank"> <img src="https://user-images.githubusercontent.com/1423657/184538094-13c11500-24ef-4468-9f33-dc9d564238e3.gif" width=700 class=border /> </a> @@ -166,7 +152,7 @@ With **qryn** and **grafana** everything _just works_ right out of the box: ------------ -📚 Follow our team _behind the scenes_ on the [qryn blog](https://blog.qryn.dev) +📚 Follow our team _behind the scenes_ on the [gigapipe blog](https://blog.gigapipe.com) ------------ @@ -192,11 +178,13 @@ Whether it's code, documentation or grammar, we ❤️ all contributions. Not su <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/0/06/AGPLv3_Logo.svg/2560px-AGPLv3_Logo.svg.png" width=200> -©️ QXIP BV, released under the GNU Affero General Public License v3.0. See [LICENSE](LICENSE) for details. +©️ GIGAPIPE, HEPVEST BV, released under the GNU Affero General Public License v3.0. See [LICENSE](LICENSE) for details. + +[^1]: Gigapipe is a 100% clear-room api implementation and does not fork, use or derivate from Grafana code or design concepts.[^1] -[^1]: qryn is not affiliated or endorsed by Grafana Labs or ClickHouse Inc. All rights belong to their respective owners. +[^2]: Gigapipe is not affiliated or endorsed by Grafana Labs or ClickHouse Inc. All rights belong to their respective owners.[^2] -[^2]: qryn is a 100% clear-room api implementation and does not fork, use or derivate from Grafana Loki code or concepts. +[^3]: Grafana®, Loki™ and Tempo® are a Trademark of Raintank, Grafana Labs. ClickHouse® is a trademark of ClickHouse Inc.[^3] -[^3]: Grafana®, Loki™ and Tempo® are a Trademark of Raintank, Grafana Labs. ClickHouse® is a trademark of ClickHouse Inc. Prometheus is a trademark of The Linux Foundation. +[^4]: Prometheus is a trademark of The Linux Foundation.[^4] diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 64729385..00000000 --- a/SECURITY.md +++ /dev/null @@ -1,14 +0,0 @@ -# Security Policy - -## Supported Versions - -| Version | Supported | -| ------- | ------------------ | -| 2.x.x | :white_check_mark: | -| 1.x.x | :x: | - -## Reporting a Vulnerability - -Please report any _(suspected)_ security vulnerabilities to info@qxip.net. You will receive a response from us within 48 hours. If the issue is confirmed, we will release a patch as soon as possible depending on complexity but historically within a few hours up to a few days. - -_Thanks You!_ diff --git a/common.js b/common.js deleted file mode 100644 index 75aa5f6f..00000000 --- a/common.js +++ /dev/null @@ -1,166 +0,0 @@ -/** - * - * @param labels {Object | string[] | string} - * @returns {Object} - */ -module.exports.parseLabels = (labels) => { - if (Array.isArray(labels)) { - return labels.reduce((sum, l) => { - sum[l[0]] = l[1] - return sum - }, {}) - } - if (typeof labels === 'object') { - return labels - } - return JSON.parse(labels) -} - -/** - * - * @param labels {Object | string[] | string} - * @returns {string} - */ -module.exports.hashLabels = (labels) => { - if (Array.isArray(labels)) { - return JSON.stringify(labels) - } - if (typeof labels === 'object' && labels !== null) { - const res = [...Object.entries(labels)] - res.sort() - return JSON.stringify(labels) - } - return labels -} - -/** - * - * @param name {string} - * @returns {boolean} - */ -function boolEnv (name) { - const boolVal = process.env[name] - if (typeof boolVal === 'undefined' || ['no', 'n', 'false', '0'].indexOf(`${boolVal}`.toLowerCase()) !== -1) { - return false - } - if (['yes', 'y', 'true', '1'].indexOf(`${boolVal}`.toLowerCase()) !== -1) { - return true - } - throw new Error(`${name} value must be one of [no, n, false, 0, yes, y, true, 1]`) -} - -module.exports.boolEnv = boolEnv - -/** - * - * @param durationStr {string} - * @returns {number} - */ -module.exports.durationToMs = (durationStr) => { - const durations = { - ns: 1 / 1000000, - us: 1 / 1000, - ms: 1, - s: 1000, - m: 60000, - h: 60000 * 60, - d: 60000 * 60 * 24, - w: 60000 * 60 * 24 * 7 - } - for (const k of Object.keys(durations)) { - const m = durationStr.match(new RegExp(`^([0-9][.0-9]*)${k}$`)) - if (m) { - return parseInt(m[1]) * durations[k] - } - } - throw new Error('Unsupported duration') -} - -/** - * - * @param durationStr {string} - * @returns {number} - */ -module.exports.durationToNs = (durationStr) => { - const durations = { - ns: 1, - us: 1000, - ms: 1000000, - s: 1000000000, - m: 60000000000, - h: 60000000000 * 60, - d: 60000000000 * 60 * 24, - w: 60000000000 * 60 * 24 * 7 - } - for (const k of Object.keys(durations)) { - const m = durationStr.match(new RegExp(`^([0-9][.0-9]*)${k}$`)) - if (m) { - return parseInt(m[1]) * durations[k] - } - } - throw new Error('Unsupported duration') -} - -module.exports.asyncLogError = async (err, logger) => { - try { - const resp = err.response || err.err.response - if (resp) { - if (typeof resp.data === 'object') { - err.responseData = '' - err.response.data.on('data', data => { err.responseData += data }) - await new Promise((resolve) => err.response.data.once('end', resolve)) - } else { - err.responseData = err.response.data - } - logger.error(err) - } - } catch (e) { - logger.error(err) - } -} - -module.exports.isOmitTablesCreation = () => boolEnv('OMIT_CREATE_TABLES') - -module.exports.LineFmtOption = () => process.env.LINE_FMT || 'handlebars' - -module.exports.errors = require('./lib/handlers/errors') -/** - * @returns {string} - */ -module.exports.samplesOrderingRule = () => { - return process.env.ADVANCED_SAMPLES_ORDERING - ? process.env.ADVANCED_SAMPLES_ORDERING - : 'timestamp_ns' -} - -/** - * @returns {boolean} - */ -module.exports.isCustomSamplesOrderingRule = () => { - return process.env.ADVANCED_SAMPLES_ORDERING && process.env.ADVANCED_SAMPLES_ORDERING !== 'timestamp_ns' -} - -module.exports.CORS = process.env.CORS_ALLOW_ORIGIN || '*' - -module.exports.clusterName = process.env.CLUSTER_NAME - -module.exports.readonly = boolEnv('READONLY') - -module.exports.bun = () => { - try { - return Bun - } catch (err) { - return false - } -} - -module.exports.logType = boolEnv('DISTINGUISH_LOGS_METRICS') ? 1 : 0 - -module.exports.metricType = boolEnv('DISTINGUISH_LOGS_METRICS') ? 2 : 0 - -module.exports.bothType = 0 - -module.exports.writerMode = (process.env.MODE === 'writer' || !process.env.MODE || process.env.MODE === 'all') && - !boolEnv('READONLY') -module.exports.readerMode = process.env.MODE === 'reader' || process.env.MODE === 'all' || boolEnv('READONLY') || - !process.env.MODE diff --git a/ctrl/logger/logger.go b/ctrl/logger/logger.go new file mode 100644 index 00000000..d6a430ed --- /dev/null +++ b/ctrl/logger/logger.go @@ -0,0 +1,67 @@ +package logger + +import ( + clconfig "github.com/metrico/cloki-config" + "github.com/sirupsen/logrus" + "io" + "log" + "os" +) + +type ILogger interface { + Error(args ...any) + Debug(args ...any) + Info(args ...any) +} + +var Logger = logrus.New() + +// initLogger function +func InitLogger(config *clconfig.ClokiConfig, output io.Writer) { + + //env := os.Getenv("environment") + //isLocalHost := env == "local" + if config.Setting.LOG_SETTINGS.Json { + // Log as JSON instead of the default ASCII formatter. + Logger.SetFormatter(&logrus.JSONFormatter{}) + } else { + Logger.Formatter.(*logrus.TextFormatter).DisableTimestamp = false + Logger.Formatter.(*logrus.TextFormatter).DisableColors = true + } + // Output to stdout instead of the default stderr + // Can be any io.Writer, see below for File example + if output != nil { + Logger.SetOutput(output) + log.SetOutput(output) + } else if config.Setting.LOG_SETTINGS.Stdout { + Logger.SetOutput(os.Stdout) + log.SetOutput(os.Stdout) + } + + /* log level default */ + if config.Setting.LOG_SETTINGS.Level == "" { + config.Setting.LOG_SETTINGS.Level = "error" + } + + if logLevel, ok := logrus.ParseLevel(config.Setting.LOG_SETTINGS.Level); ok == nil { + // Only log the warning severity or above. + Logger.SetLevel(logLevel) + } else { + Logger.Error("Couldn't parse loglevel", config.Setting.LOG_SETTINGS.Level) + Logger.SetLevel(logrus.ErrorLevel) + } + + Logger.Info("init logging system") +} + +func Debug(args ...any) { + Logger.Debug(args...) +} + +func Info(args ...any) { + Logger.Info(args...) +} + +func Error(args ...any) { + Logger.Error(args...) +} diff --git a/ctrl/main.go b/ctrl/main.go new file mode 100644 index 00000000..4efb60db --- /dev/null +++ b/ctrl/main.go @@ -0,0 +1,55 @@ +package ctrl + +import ( + "fmt" + clconfig "github.com/metrico/cloki-config" + "github.com/metrico/cloki-config/config" + "github.com/metrico/qryn/ctrl/logger" + "github.com/metrico/qryn/ctrl/qryn/maintenance" +) + +var projects = map[string]struct { + init func(*config.ClokiBaseDataBase, logger.ILogger) error + upgrade func(config []config.ClokiBaseDataBase, logger logger.ILogger) error + rotate func(base []config.ClokiBaseDataBase, logger logger.ILogger) error +}{ + "qryn": { + maintenance.InitDB, + maintenance.UpgradeAll, + maintenance.RotateAll, + }, +} + +func Init(config *clconfig.ClokiConfig, project string) error { + var err error + proj, ok := projects[project] + if !ok { + return fmt.Errorf("project %s not found", project) + } + + for _, db := range config.Setting.DATABASE_DATA { + err = proj.init(&db, logger.Logger) + if err != nil { + panic(err) + } + } + err = proj.upgrade(config.Setting.DATABASE_DATA, logger.Logger) + return err +} + +func Rotate(config *clconfig.ClokiConfig, project string) error { + var err error + proj, ok := projects[project] + if !ok { + return fmt.Errorf("project %s not found", project) + } + + for _, db := range config.Setting.DATABASE_DATA { + err = proj.init(&db, logger.Logger) + if err != nil { + panic(err) + } + } + err = proj.rotate(config.Setting.DATABASE_DATA, logger.Logger) + return err +} diff --git a/ctrl/maintenance/shared.go b/ctrl/maintenance/shared.go new file mode 100644 index 00000000..5086396e --- /dev/null +++ b/ctrl/maintenance/shared.go @@ -0,0 +1,60 @@ +package maintenance + +import ( + "context" + "crypto/tls" + "fmt" + clickhouse_v2 "github.com/ClickHouse/clickhouse-go/v2" + "github.com/metrico/cloki-config/config" + "github.com/metrico/qryn/ctrl/logger" + "time" +) + +func ConnectV2(dbObject *config.ClokiBaseDataBase, database bool) (clickhouse_v2.Conn, error) { + databaseName := "" + if database { + databaseName = dbObject.Name + } + opt := &clickhouse_v2.Options{ + Addr: []string{fmt.Sprintf("%s:%d", dbObject.Host, dbObject.Port)}, + Auth: clickhouse_v2.Auth{ + Database: databaseName, + Username: dbObject.User, + Password: dbObject.Password, + }, + Debug: dbObject.Debug, + DialTimeout: time.Second * 30, + ReadTimeout: time.Second * 30, + MaxOpenConns: 10, + MaxIdleConns: 2, + ConnMaxLifetime: time.Hour, + Settings: map[string]interface{}{ + "allow_experimental_database_replicated": "1", + "materialize_ttl_after_modify": "0", + }, + } + if dbObject.Secure { + opt.TLS = &tls.Config{InsecureSkipVerify: true} + } + return clickhouse_v2.Open(opt) +} + +func InitDBTry(conn clickhouse_v2.Conn, clusterName string, dbName string, cloud bool, logger logger.ILogger) error { + engine := "" + onCluster := "" + if clusterName != "" { + onCluster = fmt.Sprintf("ON CLUSTER `%s`", clusterName) + } + query := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s` %s %s", dbName, onCluster, engine) + logger.Info("Creating database: ", query) + err := conn.Exec(MakeTimeout(), query) + if err == nil { + return nil + } + return err +} + +func MakeTimeout() context.Context { + res, _ := context.WithTimeout(context.Background(), time.Second*30) + return res +} diff --git a/ctrl/qryn/heputils/hash.go b/ctrl/qryn/heputils/hash.go new file mode 100644 index 00000000..d73a54fa --- /dev/null +++ b/ctrl/qryn/heputils/hash.go @@ -0,0 +1,16 @@ +package heputils + +// Javascript port +func FingerprintLabelsDJBHashPrometheus(data []byte) uint32 { + + if data == nil { + return 0 + } + + var hash int32 = 5381 + + for i := len(data) - 1; i > -1; i-- { + hash = (hash * 33) ^ int32(uint16(data[i])) + } + return uint32(hash) +} diff --git a/ctrl/qryn/maintenance/codec.go b/ctrl/qryn/maintenance/codec.go new file mode 100644 index 00000000..7bcdb124 --- /dev/null +++ b/ctrl/qryn/maintenance/codec.go @@ -0,0 +1,47 @@ +package maintenance + +import ( + "context" + "fmt" + "github.com/ClickHouse/clickhouse-go/v2" +) + +func UpdateTextCodec(db clickhouse.Conn, distributed bool, newCodec string) error { + oldCodec, err := getSetting(db, distributed, "codec", "text") + if err != nil { + return err + } + if oldCodec == newCodec { + return nil + } + for _, tbl := range [][]string{ + {"tempo_traces", "payload", "String"}, + {"samples_v4", "string", "String"}, + } { + err = db.Exec(context.Background(), fmt.Sprintf("ALTER TABLE %s ALTER COLUMN `%s` TYPE %s CODEC(%s)", + tbl[0], tbl[1], tbl[2], newCodec)) + if err != nil { + return err + } + } + return putSetting(db, "codec", "text", newCodec) +} + +func UpdateLogsIndex(db clickhouse.Conn, distributed bool, newIndex string, newGranularity int) error { + idxName := fmt.Sprintf("%s GRANULARITY %d", newIndex, newGranularity) + oldIdx, err := getSetting(db, distributed, "index", "logs") + if err != nil { + return err + } + if oldIdx == idxName { + return nil + } + db.Exec(context.Background(), "ALTER TABLE samples_v4 DROP INDEX _logs_idx") + if newIndex != "" { + err = db.Exec(context.Background(), fmt.Sprintf("ALTER TABLE samples_v4 ADD INDEX _logs_idx string TYPE %s", idxName)) + if err != nil { + return err + } + } + return putSetting(db, "index", "logs", idxName) +} diff --git a/ctrl/qryn/maintenance/maintain.go b/ctrl/qryn/maintenance/maintain.go new file mode 100644 index 00000000..a58c9fbc --- /dev/null +++ b/ctrl/qryn/maintenance/maintain.go @@ -0,0 +1,163 @@ +package maintenance + +import ( + "fmt" + "github.com/metrico/cloki-config/config" + "github.com/metrico/qryn/ctrl/logger" + "github.com/metrico/qryn/ctrl/maintenance" + "strings" + "time" +) + +func upgradeDB(dbObject *config.ClokiBaseDataBase, logger logger.ILogger) error { + conn, err := maintenance.ConnectV2(dbObject, true) + if err != nil { + return err + } + mode := CLUST_MODE_SINGLE + if dbObject.Cloud { + mode = CLUST_MODE_CLOUD + } + if dbObject.ClusterName != "" { + mode |= CLUST_MODE_DISTRIBUTED + } + if dbObject.TTLDays == 0 { + return fmt.Errorf("ttl_days should be set for node#%s", dbObject.Node) + } + return Update(conn, dbObject.Name, dbObject.ClusterName, mode, dbObject.TTLDays, + dbObject.StoragePolicy, dbObject.SamplesOrdering, dbObject.SkipUnavailableShards, logger) +} + +func InitDB(dbObject *config.ClokiBaseDataBase, logger logger.ILogger) error { + if dbObject.Name == "" || dbObject.Name == "default" { + return nil + } + conn, err := maintenance.ConnectV2(dbObject, false) + if err != nil { + return err + } + defer conn.Close() + err = maintenance.InitDBTry(conn, dbObject.ClusterName, dbObject.Name, dbObject.Cloud, logger) + rows, err := conn.Query(maintenance.MakeTimeout(), fmt.Sprintf("SHOW CREATE DATABASE `%s`", dbObject.Name)) + if err != nil { + return err + } + defer rows.Close() + rows.Next() + var create string + err = rows.Scan(&create) + if err != nil { + return err + } + logger.Info(create) + return nil +} + +func TestDistributed(dbObject *config.ClokiBaseDataBase, logger logger.ILogger) (bool, error) { + if dbObject.ClusterName == "" { + return false, nil + } + conn, err := maintenance.ConnectV2(dbObject, true) + if err != nil { + return false, err + } + defer conn.Close() + onCluster := "ON CLUSTER `" + dbObject.ClusterName + "`" + logger.Info("TESTING Distributed table support") + q := fmt.Sprintf("CREATE TABLE IF NOT EXISTS dtest %s (a UInt64) Engine = Null", onCluster) + logger.Info(q) + err = conn.Exec(maintenance.MakeTimeout(), q) + if err != nil { + return false, err + } + defer conn.Exec(maintenance.MakeTimeout(), fmt.Sprintf("DROP TABLE dtest %s", onCluster)) + q = fmt.Sprintf("CREATE TABLE IF NOT EXISTS dtest_dist %s (a UInt64) Engine = Distributed('%s', '%s', 'dtest', a)", + onCluster, dbObject.ClusterName, dbObject.Name) + logger.Info(q) + err = conn.Exec(maintenance.MakeTimeout(), q) + if err != nil { + logger.Error("Distributed creation error: ", err.Error()) + if strings.Contains(err.Error(), "Only tables with a Replicated engine or tables which do not store data on disk are allowed in a Replicated database") { + logger.Info("Probably CH Cloud DEV. No Dist support.") + return false, nil + } + return false, err + } + defer conn.Exec(maintenance.MakeTimeout(), fmt.Sprintf("DROP TABLE dtest_dist %s", onCluster)) + logger.Info("Distributed support ok") + return true, nil +} + +func rotateDB(dbObject *config.ClokiBaseDataBase) error { + connDb, err := maintenance.ConnectV2(dbObject, true) + if err != nil { + return err + } + defer connDb.Close() + ttlPolicy := make([]RotatePolicy, len(dbObject.TTLPolicy)) + for i, p := range dbObject.TTLPolicy { + d, err := time.ParseDuration(p.Timeout) + if err != nil { + return err + } + ttlPolicy[i] = RotatePolicy{ + TTL: d, + MoveTo: p.MoveTo, + } + } + return Rotate(connDb, dbObject.ClusterName, dbObject.ClusterName != "", + ttlPolicy, dbObject.TTLDays, dbObject.StoragePolicy, logger.Logger) +} + +func RecodecDB(dbObject *config.ClokiBaseDataBase) error { + connDb, err := maintenance.ConnectV2(dbObject, true) + if err != nil { + return err + } + defer connDb.Close() + return UpdateTextCodec(connDb, dbObject.ClusterName != "", dbObject.TextCodec) +} + +func ReindexDB(dbObject *config.ClokiBaseDataBase) error { + connDb, err := maintenance.ConnectV2(dbObject, true) + if err != nil { + return err + } + defer connDb.Close() + return UpdateLogsIndex(connDb, dbObject.ClusterName != "", dbObject.LogsIndex, int(dbObject.LogsIndexGranularity)) +} + +func UpgradeAll(config []config.ClokiBaseDataBase, logger logger.ILogger) error { + for _, dbObject := range config { + logger.Info(fmt.Sprintf("Upgrading %s:%d/%s", dbObject.Host, dbObject.Port, dbObject.Name)) + err := upgradeDB(&dbObject, logger) + if err != nil { + return err + } + logger.Info(fmt.Sprintf("Upgrading %s:%d/%s: OK", dbObject.Host, dbObject.Port, dbObject.Name)) + } + return nil +} + +func RotateAll(base []config.ClokiBaseDataBase, logger logger.ILogger) error { + for _, dbObject := range base { + logger.Info(fmt.Sprintf("Rotating %s:%d/%s", dbObject.Host, dbObject.Port, dbObject.Name)) + err := rotateDB(&dbObject) + if err != nil { + return err + } + logger.Info(fmt.Sprintf("Rotating %s:%d/%s: OK", dbObject.Host, dbObject.Port, dbObject.Name)) + } + + /*for _, dbObject := range base { + err := RecodecDB(&dbObject) + if err != nil { + return err + } + err = ReindexDB(&dbObject) + if err != nil { + return err + } + }*/ + return nil +} diff --git a/ctrl/qryn/maintenance/rotate.go b/ctrl/qryn/maintenance/rotate.go new file mode 100644 index 00000000..9d4db42e --- /dev/null +++ b/ctrl/qryn/maintenance/rotate.go @@ -0,0 +1,204 @@ +package maintenance + +import ( + "context" + "fmt" + "github.com/ClickHouse/clickhouse-go/v2" + "github.com/metrico/qryn/ctrl/logger" + "github.com/metrico/qryn/ctrl/qryn/heputils" + "strconv" + "strings" + "time" +) + +func getSetting(db clickhouse.Conn, dist bool, tp string, name string) (string, error) { + fp := heputils.FingerprintLabelsDJBHashPrometheus([]byte( + fmt.Sprintf(`{"type":%s, "name":%s`, strconv.Quote(tp), strconv.Quote(name)), + )) + settings := "settings" + if dist { + settings += "_dist" + } + rows, err := db.Query(context.Background(), + fmt.Sprintf(`SELECT argMax(value, inserted_at) as _value FROM %s WHERE fingerprint = $1 +GROUP BY fingerprint HAVING argMax(name, inserted_at) != ''`, settings), fp) + if err != nil { + return "", err + } + res := "" + for rows.Next() { + err = rows.Scan(&res) + if err != nil { + return "", err + } + } + return res, nil +} + +func putSetting(db clickhouse.Conn, tp string, name string, value string) error { + _name := fmt.Sprintf(`{"type":%s, "name":%s`, strconv.Quote(tp), strconv.Quote(name)) + fp := heputils.FingerprintLabelsDJBHashPrometheus([]byte(_name)) + err := db.Exec(context.Background(), `INSERT INTO settings (fingerprint, type, name, value, inserted_at) +VALUES ($1, $2, $3, $4, NOW())`, fp, tp, name, value) + return err +} + +func rotateTables(db clickhouse.Conn, clusterName string, distributed bool, days []RotatePolicy, minTTL time.Duration, + insertTimeExpression string, dropTTLExpression, settingName string, + logger logger.ILogger, tables ...string) error { + var rotateTTLArr []string + for _, rp := range days { + intsevalSec := int32(rp.TTL.Seconds()) + if intsevalSec < int32(minTTL.Seconds()) { + intsevalSec = int32(minTTL.Seconds()) + } + rotateTTL := fmt.Sprintf("%s + toIntervalSecond(%d)", + insertTimeExpression, + intsevalSec) + if rp.MoveTo != "" { + rotateTTL += fmt.Sprintf(" TO DISK '" + rp.MoveTo + "'") + } + rotateTTLArr = append(rotateTTLArr, rotateTTL) + } + rotateTTLArr = append(rotateTTLArr, dropTTLExpression) + rotateTTLStr := strings.Join(rotateTTLArr, ", ") + + onCluster := "" + if clusterName != "" { + onCluster = fmt.Sprintf(" ON CLUSTER `%s` ", clusterName) + } + + val, err := getSetting(db, distributed, "rotate", settingName) + if err != nil || val == rotateTTLStr { + return err + } + for _, table := range tables { + q := fmt.Sprintf(`ALTER TABLE %s %s +MODIFY SETTING ttl_only_drop_parts = 1, merge_with_ttl_timeout = 3600, index_granularity = 8192`, table, onCluster) + logger.Debug(q) + err = db.Exec(context.Background(), q) + if err != nil { + return fmt.Errorf("query: %s\nerror: %v", q, err) + } + logger.Debug("Request OK") + logger.Debug(q) + q = fmt.Sprintf(`ALTER TABLE %s %s MODIFY TTL %s`, table, onCluster, rotateTTLStr) + err = db.Exec(context.Background(), q) + if err != nil { + return fmt.Errorf("query: %s\nerror: %v", q, err) + } + logger.Debug("Request OK") + } + return putSetting(db, "rotate", settingName, rotateTTLStr) +} + +func storagePolicyUpdate(db clickhouse.Conn, clusterName string, + distributed bool, storagePolicy string, setting string, tables ...string) error { + onCluster := "" + if clusterName != "" { + onCluster = fmt.Sprintf(" ON CLUSTER `%s` ", clusterName) + } + val, err := getSetting(db, distributed, "rotate", setting) + if err != nil || storagePolicy == "" || val == storagePolicy { + return err + } + for _, tbl := range tables { + err = db.Exec(context.Background(), fmt.Sprintf(`ALTER TABLE %s %s MODIFY SETTING storage_policy=$1`, + tbl, onCluster), storagePolicy) + if err != nil { + return err + } + } + return putSetting(db, "rotate", setting, storagePolicy) +} + +type RotatePolicy struct { + TTL time.Duration + MoveTo string +} + +func Rotate(db clickhouse.Conn, clusterName string, distributed bool, days []RotatePolicy, dropTTLDays int, + storagePolicy string, logger logger.ILogger) error { + //TODO: add pluggable extension + err := storagePolicyUpdate(db, clusterName, distributed, storagePolicy, "v3_storage_policy", + "time_series", "time_series_gin", "samples_v3") + if err != nil { + return err + } + err = storagePolicyUpdate(db, clusterName, distributed, storagePolicy, "v1_traces_storage_policy", + "tempo_traces", "tempo_traces_attrs_gin", "tempo_traces_kv") + if err != nil { + return err + } + err = storagePolicyUpdate(db, clusterName, distributed, storagePolicy, "metrics_15s", "metrics_15s") + if err != nil { + return err + } + + logDefaultTTLString := func(column string) string { + return fmt.Sprintf( + "%s + toIntervalDay(%d)", + column, dropTTLDays) + } + + tracesDefaultTTLString := func(column string) string { + return fmt.Sprintf( + "%s + toIntervalDay(%d)", + column, dropTTLDays) + } + + minTTL := time.Minute + dayTTL := time.Hour * 24 + + err = rotateTables( + db, + clusterName, + distributed, + days, + minTTL, + "toDateTime(timestamp_ns / 1000000000)", + logDefaultTTLString("toDateTime(timestamp_ns / 1000000000)"), + "v3_samples_days", logger, "samples_v3") + if err != nil { + return err + } + err = rotateTables(db, clusterName, distributed, days, + dayTTL, + "date", + logDefaultTTLString("date"), "v3_time_series_days", logger, + "time_series", "time_series_gin") + if err != nil { + return err + } + err = rotateTables(db, clusterName, distributed, days, + minTTL, + "toDateTime(timestamp_ns / 1000000000)", + tracesDefaultTTLString("toDateTime(timestamp_ns / 1000000000)"), + "v1_traces_days", + logger, "tempo_traces") + if err != nil { + return err + } + err = rotateTables(db, clusterName, distributed, days, + dayTTL, + "date", + tracesDefaultTTLString("date"), "tempo_attrs_v1", + logger, "tempo_traces_attrs_gin", "tempo_traces_kv") + if err != nil { + return err + } + if err != nil { + return err + } + err = rotateTables(db, clusterName, distributed, days, + minTTL, + "toDateTime(timestamp_ns / 1000000000)", + logDefaultTTLString("toDateTime(timestamp_ns / 1000000000)"), + "metrics_15s", + logger, "metrics_15s") + if err != nil { + return err + } + + return nil +} diff --git a/ctrl/qryn/maintenance/update.go b/ctrl/qryn/maintenance/update.go new file mode 100644 index 00000000..99c12dd9 --- /dev/null +++ b/ctrl/qryn/maintenance/update.go @@ -0,0 +1,306 @@ +package maintenance + +import ( + "bytes" + "context" + "fmt" + "github.com/ClickHouse/clickhouse-go/v2" + "github.com/metrico/qryn/ctrl/logger" + "github.com/metrico/qryn/ctrl/qryn/sql" + rand2 "math/rand" + "regexp" + "strconv" + "strings" + "text/template" + "time" +) + +const ( + CLUST_MODE_SINGLE = 1 + CLUST_MODE_CLOUD = 2 + CLUST_MODE_DISTRIBUTED = 4 +) + +func Update(db clickhouse.Conn, dbname string, clusterName string, mode int, + ttlDays int, storagePolicy string, advancedSamplesOrdering string, skipUnavailableShards bool, + logger logger.ILogger) error { + checkMode := func(m int) bool { return mode&m == m } + var err error + if err != nil { + return err + } + err = updateScripts(db, dbname, clusterName, 1, sql.LogScript, checkMode(CLUST_MODE_CLOUD), + ttlDays, storagePolicy, advancedSamplesOrdering, skipUnavailableShards, logger) + if err != nil { + return err + } + if checkMode(CLUST_MODE_DISTRIBUTED) { + err = updateScripts(db, dbname, clusterName, 3, sql.LogDistScript, + checkMode(CLUST_MODE_CLOUD), ttlDays, storagePolicy, advancedSamplesOrdering, skipUnavailableShards, logger) + if err != nil { + return err + } + } + err = updateScripts(db, dbname, clusterName, 2, sql.TracesScript, + checkMode(CLUST_MODE_CLOUD), ttlDays, storagePolicy, advancedSamplesOrdering, skipUnavailableShards, logger) + if err != nil { + return err + } + if checkMode(CLUST_MODE_DISTRIBUTED) { + err = updateScripts(db, dbname, clusterName, 4, sql.TracesDistScript, + checkMode(CLUST_MODE_CLOUD), ttlDays, storagePolicy, advancedSamplesOrdering, skipUnavailableShards, logger) + if err != nil { + return err + } + } + + err = updateScripts(db, dbname, clusterName, 5, sql.ProfilesScript, + checkMode(CLUST_MODE_CLOUD), ttlDays, storagePolicy, advancedSamplesOrdering, skipUnavailableShards, logger) + if err != nil { + return err + } + if checkMode(CLUST_MODE_DISTRIBUTED) { + err = updateScripts(db, dbname, clusterName, 6, sql.ProfilesDistScript, + checkMode(CLUST_MODE_CLOUD), ttlDays, storagePolicy, advancedSamplesOrdering, skipUnavailableShards, logger) + if err != nil { + return err + } + } + + err = Cleanup(db, clusterName, checkMode(CLUST_MODE_DISTRIBUTED), dbname, logger) + + return err +} + +func getSQLFile(strContents string) ([]string, error) { + var res []string + strContents = regexp.MustCompile("(?m)^\\s+$").ReplaceAllString(strContents, "") + strContents = regexp.MustCompile("(?m)^##.*$").ReplaceAllString(strContents, "") + _res := strings.Split(strContents, ";\n\n") + for _, req := range _res { + _req := strings.Trim(req, "\n ") + if _req == "" { + continue + } + res = append(res, _req) + } + return res, nil +} + +func getDBExec(db clickhouse.Conn, env map[string]string, logger logger.ILogger) func(query string, args ...[]interface{}) error { + rand := rand2.New(rand2.NewSource(time.Now().UnixNano())) + return func(query string, args ...[]interface{}) error { + name := fmt.Sprintf("tpl_%d", rand.Uint64()) + tpl, err := template.New(name).Parse(query) + if err != nil { + logger.Error(query) + return err + } + buf := bytes.NewBuffer(nil) + err = tpl.Execute(buf, env) + if err != nil { + logger.Error(query) + return err + } + req := buf.String() + err = db.Exec(context.Background(), req) + if err != nil { + logger.Error(req) + return err + } + return nil + } +} + +func updateScripts(db clickhouse.Conn, dbname string, clusterName string, k int64, file string, replicated bool, + ttlDays int, storagePolicy string, advancedSamplesOrdering string, skipUnavailableShards bool, logger logger.ILogger) error { + scripts, err := getSQLFile(file) + verTable := "ver" + env := map[string]string{ + "DB": dbname, + "CLUSTER": clusterName, + "OnCluster": " ", + "DefaultTtlDays": "30", + "CREATE_SETTINGS": "", + "SAMPLES_ORDER_RUL": "timestamp_ns", + "DIST_CREATE_SETTINGS": "", + } + if storagePolicy != "" { + env["CREATE_SETTINGS"] = fmt.Sprintf("SETTINGS storage_policy = '%s'", storagePolicy) + } + //TODO: move to the config package as it should be: os.Getenv("ADVANCED_SAMPLES_ORDERING") + if advancedSamplesOrdering != "" { + env["SAMPLES_ORDER_RUL"] = advancedSamplesOrdering + } + //TODO: move to the config package + if skipUnavailableShards { + env["DIST_CREATE_SETTINGS"] += fmt.Sprintf(" SETTINGS skip_unavailable_shards = 1") + } + if ttlDays != 0 { + env["DefaultTtlDays"] = strconv.FormatInt(int64(ttlDays), 10) + } + + if clusterName != "" { + env["OnCluster"] = "ON CLUSTER `" + clusterName + "`" + } + if replicated { + env["ReplacingMergeTree"] = "ReplicatedReplacingMergeTree" + env["MergeTree"] = "ReplicatedMergeTree" + env["AggregatingMergeTree"] = "ReplicatedAggregatingMergeTree" + } else { + env["ReplacingMergeTree"] = "ReplacingMergeTree" + env["MergeTree"] = "MergeTree" + env["AggregatingMergeTree"] = "AggregatingMergeTree" + } + exec := getDBExec(db, env, logger) + err = exec(`CREATE TABLE IF NOT EXISTS ver {{.OnCluster}} (k UInt64, ver UInt64) +ENGINE={{.ReplacingMergeTree}}(ver) ORDER BY k`) + if err != nil { + return err + } + if clusterName != "" { + err = exec(`CREATE TABLE IF NOT EXISTS ver_dist {{.OnCluster}} (k UInt64, ver UInt64) +ENGINE=Distributed('{{.CLUSTER}}','{{.DB}}', 'ver', rand())`) + if err != nil { + return err + } + verTable = "ver_dist" + } + var ver uint64 = 0 + if k >= 0 { + rows, err := db.Query(context.Background(), + fmt.Sprintf("SELECT max(ver) as ver FROM %s WHERE k = $1 FORMAT JSON", verTable), k) + if err != nil { + return err + } + + for rows.Next() { + err = rows.Scan(&ver) + if err != nil { + return err + } + } + } + for i := ver; i < uint64(len(scripts)); i++ { + logger.Info(fmt.Sprintf("Upgrade v.%d to v.%d ", i, i+1)) + err = exec(scripts[i]) + if err != nil { + logger.Error(scripts[i]) + return err + } + err = db.Exec(context.Background(), "INSERT INTO ver (k, ver) VALUES ($1, $2)", k, i+1) + if err != nil { + return err + } + logger.Info(fmt.Sprintf("Upgrade v.%d to v.%d ok", i, i+1)) + } + return nil +} + +func tableExists(db clickhouse.Conn, name string) (bool, error) { + rows, err := db.Query(context.Background(), "SHOW TABLES") + if err != nil { + return false, err + } + defer rows.Close() + for rows.Next() { + var _name string + err = rows.Scan(&_name) + if err != nil { + return false, err + } + if _name == name { + return true, nil + } + } + return false, nil +} + +func tableEmpty(db clickhouse.Conn, name string) (bool, error) { + rows, err := db.Query(context.Background(), fmt.Sprintf("SELECT count(1) FROM %s", name)) + if err != nil { + return false, err + } + defer rows.Close() + rows.Next() + var count uint64 + err = rows.Scan(&count) + return count == 0, err +} + +func isExistsAndEmpty(db clickhouse.Conn, name string) (bool, error) { + exists, err := tableExists(db, name) + if err != nil { + return false, err + } + if !exists { + return false, nil + } + empty, err := tableEmpty(db, name) + return empty, err +} + +func Cleanup(db clickhouse.Conn, clusterName string, distributed bool, dbname string, logger logger.ILogger) error { + //TODO: add plugin extension + env := map[string]string{ + "DB": dbname, + "CLUSTER": clusterName, + "OnCluster": " ", + "DefaultTtlDays": "30", + } + + if clusterName != "" { + env["OnCluster"] = "ON CLUSTER `" + clusterName + "`" + } + + tableDeps := []struct { + name []string + depsTables []string + depsViews []string + }{ + { + []string{}, + []string{}, + []string{}, + }, + } + + exec := getDBExec(db, env, logger) + + for _, dep := range tableDeps { + mainExists := false + for _, main := range dep.name { + existsAndEmpty, err := isExistsAndEmpty(db, main) + if err != nil { + return err + } + if existsAndEmpty { + err = exec(fmt.Sprintf("DROP TABLE IF EXISTS %s {{.OnCluster}}", main)) + if err != nil { + return err + } + } + exists, err := tableExists(db, main) + if err != nil { + return err + } + mainExists = mainExists || exists + } + if mainExists { + continue + } + for _, tbl := range dep.depsTables { + err := exec(fmt.Sprintf("DROP TABLE IF EXISTS %s {{.OnCluster}}", tbl)) + if err != nil { + return err + } + } + for _, view := range dep.depsViews { + err := db.Exec(context.Background(), fmt.Sprintf("DROP VIEW IF EXISTS %s {{.OnCluster}}", view)) + if err != nil { + return err + } + } + } + return nil +} diff --git a/ctrl/qryn/sql/log.sql b/ctrl/qryn/sql/log.sql new file mode 100644 index 00000000..daaabcfb --- /dev/null +++ b/ctrl/qryn/sql/log.sql @@ -0,0 +1,172 @@ +## Comments are started with `##` +## The file is for log replicated tables +## Queries are separated with ";" and one empty string +## APPEND ONLY!!!!! +## Templating tokens: +## templating is done by "text/template" go lib +## {{.OnCluster}} - is replaced by "ON CLUSTER `<clustername>`" or an empty string +## {{.ReplacingMergeTree}} - is replaced by ReplacingMergeTree or ReplicatedReplacingMergeTree +## {{.MergeTree}} - is replaced by MergeTree or ReplicatedMergeTree +## {{.AggregatingMergeTree}} - is replaced by AggregatingMergeTree or ReplicatedAggregatingMergeTree +## {{.CLUSTER}} - is replaced by cluster name if needed +## {{.DB}} - is replaced by the db name +## {{.CREATE_SETTINGS}} - extra create settings for tables //TODO +## {{.SAMPLES_ORDER_RUL}} - samples ordering rule configurable //TODO + +CREATE TABLE IF NOT EXISTS {{.DB}}.time_series {{.OnCluster}} ( + date Date, + fingerprint UInt64, + labels String, + name String +) ENGINE = {{.ReplacingMergeTree}}(date) +PARTITION BY date +ORDER BY fingerprint {{.CREATE_SETTINGS}}; + +CREATE TABLE IF NOT EXISTS {{.DB}}.samples_v3 {{.OnCluster}} ( + fingerprint UInt64, + timestamp_ns Int64 CODEC(DoubleDelta), + value Float64 CODEC(Gorilla), + string String +) ENGINE = {{.MergeTree}} +PARTITION BY toStartOfDay(toDateTime(timestamp_ns / 1000000000)) +ORDER BY ({{.SAMPLES_ORDER_RUL}}) {{.CREATE_SETTINGS}}; + +CREATE TABLE IF NOT EXISTS {{.DB}}.settings {{.OnCluster}} ( + fingerprint UInt64, + type String, + name String, + value String, + inserted_at DateTime64(9, 'UTC') +) ENGINE = {{.ReplacingMergeTree}}(inserted_at) +ORDER BY fingerprint {{.CREATE_SETTINGS}}; + +DROP TABLE IF EXISTS {{.DB}}.samples_read {{.OnCluster}}; + +CREATE TABLE IF NOT EXISTS {{.DB}}.samples_read {{.OnCluster}} ( + fingerprint UInt64, + timestamp_ms Int64, + value Float64, + string String +) ENGINE=Merge('{{.DB}}', '^(samples|samples_v2)$'); + +CREATE VIEW IF NOT EXISTS {{.DB}}.samples_read_v2_1 {{.OnCluster}} AS +SELECT fingerprint, timestamp_ms * 1000000 as timestamp_ns, value, string FROM samples_read; + +CREATE TABLE IF NOT EXISTS {{.DB}}.samples_read_v2_2 {{.OnCluster}} ( + fingerprint UInt64, + timestamp_ns Int64, + value Float64, + string String +) ENGINE=Merge('{{.DB}}', '^(samples_read_v2_1|samples_v3)$'); + +CREATE TABLE IF NOT EXISTS {{.DB}}.time_series_gin {{.OnCluster}} ( + date Date, + key String, + val String, + fingerprint UInt64 +) ENGINE = {{.ReplacingMergeTree}}() +PARTITION BY date +ORDER BY (key, val, fingerprint) {{.CREATE_SETTINGS}}; + +CREATE MATERIALIZED VIEW IF NOT EXISTS {{.DB}}.time_series_gin_view {{.OnCluster}} TO time_series_gin +AS SELECT + date, + pairs.1 as key, + pairs.2 as val, + fingerprint +FROM time_series +ARRAY JOIN JSONExtractKeysAndValues(time_series.labels, 'String') as pairs; + +INSERT INTO {{.DB}}.settings (fingerprint, type, name, value, inserted_at) +VALUES (cityHash64('update_v3_5'), 'update', 'v3_1', toString(toUnixTimestamp(NOW())), NOW()); + +CREATE TABLE IF NOT EXISTS {{.DB}}.metrics_15s {{.OnCluster}} ( + fingerprint UInt64, + timestamp_ns Int64 CODEC(DoubleDelta), + last AggregateFunction(argMax, Float64, Int64), + max SimpleAggregateFunction(max, Float64), + min SimpleAggregateFunction(min, Float64), + count AggregateFunction(count), + sum SimpleAggregateFunction(sum, Float64), + bytes SimpleAggregateFunction(sum, Float64) +) ENGINE = {{.AggregatingMergeTree}} +PARTITION BY toDate(toDateTime(intDiv(timestamp_ns, 1000000000))) +ORDER BY (fingerprint, timestamp_ns) {{.CREATE_SETTINGS}}; + +CREATE MATERIALIZED VIEW IF NOT EXISTS {{.DB}}.metrics_15s_mv {{.OnCluster}} TO metrics_15s +AS SELECT + fingerprint, + intDiv(samples.timestamp_ns, 15000000000) * 15000000000 as timestamp_ns, + argMaxState(value, samples.timestamp_ns) as last, + maxSimpleState(value) as max, + minSimpleState(value) as min, + countState() as count, + sumSimpleState(value) as sum, + sumSimpleState(length(string)) as bytes +FROM {{.DB}}.samples_v3 as samples +GROUP BY fingerprint, timestamp_ns; + +INSERT INTO {{.DB}}.settings (fingerprint, type, name, value, inserted_at) +VALUES (cityHash64('update_v3_2'), 'update', 'v3_2', toString(toUnixTimestamp(NOW())), NOW()); + +INSERT INTO {{.DB}}.settings (fingerprint, type, name, value, inserted_at) +VALUES (cityHash64('update_v3_2'), 'update', 'v3_2', toString(toUnixTimestamp(NOW())), NOW()); + +ALTER TABLE {{.DB}}.time_series {{.OnCluster}} + ADD COLUMN IF NOT EXISTS type UInt8, + MODIFY ORDER BY (fingerprint, type); + +ALTER TABLE {{.DB}}.samples_v3 {{.OnCluster}} + ADD COLUMN IF NOT EXISTS type UInt8; + +ALTER TABLE {{.DB}}.time_series_gin {{.OnCluster}} + ADD COLUMN IF NOT EXISTS type UInt8, + MODIFY ORDER BY (key, val, fingerprint, type); + +ALTER TABLE {{.DB}}.metrics_15s {{.OnCluster}} + ADD COLUMN IF NOT EXISTS type UInt8, + MODIFY ORDER BY (fingerprint, timestamp_ns, type); + +RENAME TABLE {{.DB}}.time_series_gin_view TO time_series_gin_view_bak {{.OnCluster}}; + +CREATE MATERIALIZED VIEW IF NOT EXISTS {{.DB}}.time_series_gin_view {{.OnCluster}} TO time_series_gin +AS SELECT + date, + pairs.1 as key, + pairs.2 as val, + fingerprint, + type +FROM time_series +ARRAY JOIN JSONExtractKeysAndValues(time_series.labels, 'String') as pairs; + +DROP TABLE IF EXISTS {{.DB}}.time_series_gin_view_bak {{.OnCluster}}; + +RENAME TABLE {{.DB}}.metrics_15s_mv TO metrics_15s_mv_bak {{.OnCluster}}; + +CREATE MATERIALIZED VIEW IF NOT EXISTS {{.DB}}.metrics_15s_mv {{.OnCluster}} TO metrics_15s +AS SELECT + fingerprint, + intDiv(samples.timestamp_ns, 15000000000) * 15000000000 as timestamp_ns, + argMaxState(value, samples.timestamp_ns) as last, + maxSimpleState(value) as max, + minSimpleState(value) as min, + countState() as count, + sumSimpleState(value) as sum, + sumSimpleState(length(string)) as bytes, + type +FROM samples_v3 as samples +GROUP BY fingerprint, timestamp_ns, type; + +DROP TABLE IF EXISTS {{.DB}}.metrics_15s_mv_bak {{.OnCluster}}; + +ALTER TABLE time_series + (ADD COLUMN `type_v2` UInt8 ALIAS type); + +ALTER TABLE time_series_gin + (ADD COLUMN `type_v2` UInt8 ALIAS type); + +ALTER TABLE samples_v3 + (ADD COLUMN `type_v2` UInt8 ALIAS type); + +ALTER TABLE metrics_15s + (ADD COLUMN `type_v2` UInt8 ALIAS type); diff --git a/ctrl/qryn/sql/log_dist.sql b/ctrl/qryn/sql/log_dist.sql new file mode 100644 index 00000000..acd93ddd --- /dev/null +++ b/ctrl/qryn/sql/log_dist.sql @@ -0,0 +1,65 @@ +## These are comments +## The file is for log distributed tables +## Queries are separated with ";" and one empty string +## APPEND ONLY!!!!! +## Templating tokens: see log.sql + +CREATE TABLE IF NOT EXISTS {{.DB}}.metrics_15s_dist {{.OnCluster}} ( + `fingerprint` UInt64, + `timestamp_ns` Int64 CODEC(DoubleDelta), + `last` AggregateFunction(argMax, Float64, Int64), + `max` SimpleAggregateFunction(max, Float64), + `min` SimpleAggregateFunction(min, Float64), + `count` AggregateFunction(count), + `sum` SimpleAggregateFunction(sum, Float64), + `bytes` SimpleAggregateFunction(sum, Float64) +) ENGINE = Distributed('{{.CLUSTER}}', '{{.DB}}', 'metrics_15s', fingerprint) {{.DIST_CREATE_SETTINGS}}; + +CREATE TABLE IF NOT EXISTS {{.DB}}.samples_v3_dist {{.OnCluster}} ( + `fingerprint` UInt64, + `timestamp_ns` Int64 CODEC(DoubleDelta), + `value` Float64 CODEC(Gorilla), + `string` String +) ENGINE = Distributed('{{.CLUSTER}}','{{.DB}}', 'samples_v3', fingerprint); + +CREATE TABLE IF NOT EXISTS {{.DB}}.time_series_dist {{.OnCluster}} ( + `date` Date, + `fingerprint` UInt64, + `labels` String, + `name` String +) ENGINE = Distributed('{{.CLUSTER}}','{{.DB}}', 'time_series', fingerprint) {{.DIST_CREATE_SETTINGS}}; + +CREATE TABLE IF NOT EXISTS {{.DB}}.settings_dist {{.OnCluster}} ( + `fingerprint` UInt64, + `type` String, + `name` String, + `value` String, + `inserted_at` DateTime64(9, 'UTC') +) ENGINE = Distributed('{{.CLUSTER}}','{{.DB}}', 'settings', rand()) {{.DIST_CREATE_SETTINGS}}; + +CREATE TABLE IF NOT EXISTS {{.DB}}.time_series_gin_dist {{.OnCluster}} ( + date Date, + key String, + val String, + fingerprint UInt64 +) ENGINE = Distributed('{{.CLUSTER}}','{{.DB}}', 'time_series_gin', rand()) {{.DIST_CREATE_SETTINGS}}; + +ALTER TABLE {{.DB}}.metrics_15s_dist {{.OnCluster}} ADD COLUMN IF NOT EXISTS `type` UInt8; + +ALTER TABLE {{.DB}}.samples_v3_dist {{.OnCluster}} ADD COLUMN IF NOT EXISTS `type` UInt8; + +ALTER TABLE {{.DB}}.time_series_dist {{.OnCluster}} ADD COLUMN IF NOT EXISTS `type` UInt8; + +ALTER TABLE {{.DB}}.time_series_gin_dist {{.OnCluster}} ADD COLUMN IF NOT EXISTS `type` UInt8; + +ALTER TABLE time_series_dist + (ADD COLUMN `type_v2` UInt8 ALIAS type); + +ALTER TABLE time_series_gin_dist + (ADD COLUMN `type_v2` UInt8 ALIAS type); + +ALTER TABLE samples_v3_dist + (ADD COLUMN `type_v2` UInt8 ALIAS type); + +ALTER TABLE metrics_15s_dist + (ADD COLUMN `type_v2` UInt8 ALIAS type); \ No newline at end of file diff --git a/ctrl/qryn/sql/profiles.sql b/ctrl/qryn/sql/profiles.sql new file mode 100644 index 00000000..1683fcc5 --- /dev/null +++ b/ctrl/qryn/sql/profiles.sql @@ -0,0 +1,150 @@ +## Scripts for the profiles support +## APPEND ONLY!!! +## Please check log.sql file to get the main rules and template substarctions + +CREATE TABLE IF NOT EXISTS {{.DB}}.profiles_input {{.OnCluster}} ( + timestamp_ns UInt64, + type LowCardinality(String), + service_name LowCardinality(String), + sample_types_units Array(Tuple(String, String)), + period_type LowCardinality(String), + period_unit LowCardinality(String), + tags Array(Tuple(String, String)), + duration_ns UInt64, + payload_type LowCardinality(String), + payload String, + values_agg Array(Tuple(String, Int64, Int32)) CODEC(ZSTD(1)) +) Engine=Null; + +CREATE TABLE IF NOT EXISTS {{.DB}}.profiles {{.OnCluster}} ( + timestamp_ns UInt64 CODEC(DoubleDelta, ZSTD(1)), + fingerprint UInt64 CODEC(DoubleDelta, ZSTD(1)), + type_id LowCardinality(String) CODEC(ZSTD(1)), + sample_types_units Array(Tuple(String, String)) CODEC(ZSTD(1)), + service_name LowCardinality(String) CODEC(ZSTD(1)), + duration_ns UInt64 CODEC(DoubleDelta, ZSTD(1)), + payload_type LowCardinality(String) CODEC(ZSTD(1)), + payload String CODEC(ZSTD(1)), + values_agg Array(Tuple(String, Int64, Int32)) CODEC(ZSTD(1)) +) Engine {{.MergeTree}}() +ORDER BY (type_id, service_name, timestamp_ns) +PARTITION BY toDate(FROM_UNIXTIME(intDiv(timestamp_ns, 1000000000))) {{.CREATE_SETTINGS}}; + +CREATE MATERIALIZED VIEW IF NOT EXISTS {{.DB}}.profiles_mv {{.OnCluster}} TO profiles AS +SELECT + timestamp_ns, + cityHash64(arraySort(arrayConcat( + profiles_input.tags, [ + ('__type__', concatWithSeparator(':', type, period_type, period_unit) as _type_id), + ('__sample_types_units__', arrayStringConcat(arrayMap(x -> x.1 || ':' || x.2, arraySort(sample_types_units)), ';')), + ('service_name', service_name) + ])) as _tags) as fingerprint, + _type_id as type_id, + sample_types_units, + service_name, + duration_ns, + payload_type, + payload, + values_agg +FROM profiles_input; + +CREATE TABLE IF NOT EXISTS {{.DB}}.profiles_series {{.OnCluster}} ( + date Date CODEC(ZSTD(1)), + type_id LowCardinality(String) CODEC(ZSTD(1)), + sample_types_units Array(Tuple(String, String)) CODEC(ZSTD(1)), + service_name LowCardinality(String) CODEC(ZSTD(1)), + fingerprint UInt64 CODEC(DoubleDelta, ZSTD(1)), + tags Array(Tuple(String, String)) CODEC(ZSTD(1)) +) Engine {{.ReplacingMergeTree}}() +ORDER BY (date, type_id, fingerprint) +PARTITION BY date {{.CREATE_SETTINGS}}; + +CREATE MATERIALIZED VIEW IF NOT EXISTS {{.DB}}.profiles_series_mv {{.OnCluster}} TO profiles_series AS +SELECT + toDate(intDiv(timestamp_ns, 1000000000)) as date, + concatWithSeparator(':', type, period_type, period_unit) as type_id, + sample_types_units, + service_name, + cityHash64(arraySort(arrayConcat( + profiles_input.tags, [ + ('__type__', type_id), + ('__sample_types_units__', arrayStringConcat(arrayMap(x -> x.1 || ':' || x.2, arraySort(sample_types_units)), ';')), + ('service_name', service_name) + ])) as _tags) as fingerprint, + arrayConcat(profiles_input.tags, [('service_name', service_name)]) as tags +FROM profiles_input; + +CREATE TABLE IF NOT EXISTS {{.DB}}.profiles_series_gin {{.OnCluster}} ( + date Date CODEC(ZSTD(1)), + key String CODEC(ZSTD(1)), + val String CODEC(ZSTD(1)), + type_id LowCardinality(String) CODEC(ZSTD(1)), + sample_types_units Array(Tuple(String, String)), + service_name LowCardinality(String) CODEC(ZSTD(1)), + fingerprint UInt64 CODEC(DoubleDelta, ZSTD(1)) +) Engine {{.ReplacingMergeTree}}() +ORDER BY (date, key, val, type_id, fingerprint) +PARTITION BY date {{.CREATE_SETTINGS}}; + +CREATE MATERIALIZED VIEW IF NOT EXISTS {{.DB}}.profiles_series_gin_mv {{.OnCluster}} TO profiles_series_gin AS +SELECT + date, + kv.1 as key, + kv.2 as val, + type_id, + sample_types_units, + service_name, + fingerprint +FROM profiles_series ARRAY JOIN tags as kv; + +CREATE TABLE IF NOT EXISTS {{.DB}}.profiles_series_keys {{.OnCluster}} ( + date Date, + key String, + val String, + val_id UInt64 +) Engine {{.ReplacingMergeTree}}() +ORDER BY (date, key, val_id) +PARTITION BY date {{.CREATE_SETTINGS}}; + +CREATE MATERIALIZED VIEW IF NOT EXISTS {{.DB}}.profiles_series_keys_mv {{.OnCluster}} TO profiles_series_keys AS +SELECT + date, + key, + val, + cityHash64(val) % 50000 as val_id +FROM profiles_series_gin; + +ALTER TABLE {{.DB}}.profiles_input {{.OnCluster}} + ADD COLUMN IF NOT EXISTS `tree` Array(Tuple(UInt64, UInt64, UInt64, Array(Tuple(String, Int64, Int64)))), + ADD COLUMN IF NOT EXISTS `functions` Array(Tuple(UInt64, String)); + +ALTER TABLE {{.DB}}.profiles {{.OnCluster}} + ADD COLUMN IF NOT EXISTS `tree` Array(Tuple(UInt64, UInt64, UInt64, Array(Tuple(String, Int64, Int64)))), + ADD COLUMN IF NOT EXISTS `functions` Array(Tuple(UInt64, String)); + +RENAME TABLE IF EXISTS {{.DB}}.profiles_mv TO profiles_mv_bak {{.OnCluster}}; + +CREATE MATERIALIZED VIEW IF NOT EXISTS {{.DB}}.profiles_mv {{.OnCluster}} TO profiles AS +SELECT + timestamp_ns, + cityHash64(arraySort(arrayConcat( + profiles_input.tags, [ + ('__type__', concatWithSeparator(':', type, period_type, period_unit) as _type_id), + ('__sample_types_units__', arrayStringConcat(arrayMap(x -> x.1 || ':' || x.2, arraySort(sample_types_units)), ';')), + ('service_name', service_name) + ])) as _tags) as fingerprint, + _type_id as type_id, + sample_types_units, + service_name, + duration_ns, + payload_type, + payload, + values_agg, + tree, + functions +FROM profiles_input; + +DROP TABLE IF EXISTS {{.DB}}.profiles_mv_bak {{.OnCluster}}; + +INSERT INTO {{.DB}}.settings (fingerprint, type, name, value, inserted_at) +VALUES (cityHash64('profiles_v2'), 'update', 'profiles_v2', toString(toUnixTimestamp(NOW())), NOW()); \ No newline at end of file diff --git a/ctrl/qryn/sql/profiles_dist.sql b/ctrl/qryn/sql/profiles_dist.sql new file mode 100644 index 00000000..b66a4ace --- /dev/null +++ b/ctrl/qryn/sql/profiles_dist.sql @@ -0,0 +1,51 @@ +## Scripts for the distributed profiles support +## APPEND ONLY!!! +## Please check log.sql file to get the main rules and template substarctions + +CREATE TABLE IF NOT EXISTS {{.DB}}.profiles_dist {{.OnCluster}} ( + timestamp_ns UInt64, + fingerprint UInt64, + type_id LowCardinality(String), + service_name LowCardinality(String), + duration_ns UInt64, + payload_type LowCardinality(String), + payload String, + values_agg Array(Tuple(String, Int64, Int32)) +) ENGINE = Distributed('{{.CLUSTER}}','{{.DB}}','profiles', fingerprint) {{.DIST_CREATE_SETTINGS}}; + +CREATE TABLE IF NOT EXISTS {{.DB}}.profiles_series_dist {{.OnCluster}} ( + date Date, + type_id LowCardinality(String), + service_name LowCardinality(String), + fingerprint UInt64 CODEC(DoubleDelta, ZSTD(1)), + tags Array(Tuple(String, String)) CODEC(ZSTD(1)) +) ENGINE = Distributed('{{.CLUSTER}}','{{.DB}}','profiles_series',fingerprint) {{.DIST_CREATE_SETTINGS}}; + +CREATE TABLE IF NOT EXISTS {{.DB}}.profiles_series_gin_dist {{.OnCluster}} ( + date Date, + key String, + val String, + type_id LowCardinality(String), + service_name LowCardinality(String), + fingerprint UInt64 CODEC(DoubleDelta, ZSTD(1)) +) ENGINE = Distributed('{{.CLUSTER}}','{{.DB}}','profiles_series_gin',fingerprint) {{.DIST_CREATE_SETTINGS}}; + +CREATE TABLE IF NOT EXISTS {{.DB}}.profiles_series_keys_dist {{.OnCluster}} ( + date Date, + key String, + val String, + val_id UInt64 +) ENGINE = Distributed('{{.CLUSTER}}','{{.DB}}','profiles_series_keys', rand()) {{.DIST_CREATE_SETTINGS}}; + +ALTER TABLE {{.DB}}.profiles_dist {{.OnCluster}} + ADD COLUMN IF NOT EXISTS `tree` Array(Tuple(UInt64, UInt64, UInt64, Array(Tuple(String, Int64, Int64)))), + ADD COLUMN IF NOT EXISTS `functions` Array(Tuple(UInt64, String)); + +ALTER TABLE {{.DB}}.profiles_dist {{.OnCluster}} + ADD COLUMN IF NOT EXISTS `sample_types_units` Array(Tuple(String, String)); + +ALTER TABLE {{.DB}}.profiles_series_dist {{.OnCluster}} + ADD COLUMN IF NOT EXISTS `sample_types_units` Array(Tuple(String, String)); + +ALTER TABLE {{.DB}}.profiles_series_gin_dist {{.OnCluster}} + ADD COLUMN IF NOT EXISTS `sample_types_units` Array(Tuple(String, String)); \ No newline at end of file diff --git a/ctrl/qryn/sql/sql.go b/ctrl/qryn/sql/sql.go new file mode 100644 index 00000000..83b4d202 --- /dev/null +++ b/ctrl/qryn/sql/sql.go @@ -0,0 +1,21 @@ +package sql + +import _ "embed" + +//go:embed log.sql +var LogScript string + +//go:embed log_dist.sql +var LogDistScript string + +//go:embed traces.sql +var TracesScript string + +//go:embed traces_dist.sql +var TracesDistScript string + +//go:embed profiles.sql +var ProfilesScript string + +//go:embed profiles_dist.sql +var ProfilesDistScript string diff --git a/ctrl/qryn/sql/traces.sql b/ctrl/qryn/sql/traces.sql new file mode 100644 index 00000000..6c5a96ad --- /dev/null +++ b/ctrl/qryn/sql/traces.sql @@ -0,0 +1,92 @@ +## These are comments +## The file for traces +## Queries are separated with ";" and one empty string +## APPEND ONLY!!!!! +## Templating tokens: see log.sql + +CREATE TABLE IF NOT EXISTS {{.DB}}.tempo_traces {{.OnCluster}} ( + oid String DEFAULT '0', + trace_id FixedString(16), + span_id FixedString(8), + parent_id String, + name String, + timestamp_ns Int64 CODEC(DoubleDelta), + duration_ns Int64, + service_name String, + payload_type Int8, + payload String +) Engine = {{.MergeTree}}() ORDER BY (oid, trace_id, timestamp_ns) +PARTITION BY (oid, toDate(FROM_UNIXTIME(intDiv(timestamp_ns, 1000000000)))) {{.CREATE_SETTINGS}}; + +CREATE TABLE IF NOT EXISTS {{.DB}}.tempo_traces_attrs_gin {{.OnCluster}} ( + oid String, + date Date, + key String, + val String, + trace_id FixedString(16), + span_id FixedString(8), + timestamp_ns Int64, + duration Int64 +) Engine = {{.ReplacingMergeTree}}() +PARTITION BY date +ORDER BY (oid, date, key, val, timestamp_ns, trace_id, span_id) {{.CREATE_SETTINGS}}; + +CREATE TABLE IF NOT EXISTS {{.DB}}.tempo_traces_kv {{.OnCluster}} ( + oid String, + date Date, + key String, + val_id UInt64, + val String +) Engine = {{.ReplacingMergeTree}}() +PARTITION BY (oid, date) +ORDER BY (oid, date, key, val_id) {{.CREATE_SETTINGS}}; + +CREATE MATERIALIZED VIEW IF NOT EXISTS {{.DB}}.tempo_traces_kv_mv {{.OnCluster}} TO tempo_traces_kv AS +SELECT + oid, + date, + key, + cityHash64(val) % 10000 as val_id, + val +FROM tempo_traces_attrs_gin; + +CREATE TABLE IF NOT EXISTS {{.DB}}.traces_input {{.OnCluster}} ( + oid String DEFAULT '0', + trace_id String, + span_id String, + parent_id String, + name String, + timestamp_ns Int64 CODEC(DoubleDelta), + duration_ns Int64, + service_name String, + payload_type Int8, + payload String, + tags Array(Tuple(String, String)) +) Engine=Null; + +CREATE MATERIALIZED VIEW IF NOT EXISTS {{.DB}}.traces_input_traces_mv {{.OnCluster}} TO tempo_traces AS +SELECT oid, + unhex(trace_id)::FixedString(16) as trace_id, + unhex(span_id)::FixedString(8) as span_id, + unhex(parent_id) as parent_id, + name, + timestamp_ns, + duration_ns, + service_name, + payload_type, + payload +FROM traces_input; + +CREATE MATERIALIZED VIEW IF NOT EXISTS {{.DB}}.traces_input_tags_mv {{.OnCluster}} TO tempo_traces_attrs_gin AS +SELECT oid, + toDate(intDiv(timestamp_ns, 1000000000)) as date, + tags.1 as key, + tags.2 as val, + unhex(trace_id)::FixedString(16) as trace_id, + unhex(span_id)::FixedString(8) as span_id, + timestamp_ns, + duration_ns as duration +FROM traces_input ARRAY JOIN tags; + +INSERT INTO {{.DB}}.settings (fingerprint, type, name, value, inserted_at) +VALUES (cityHash64('tempo_traces_v1'), 'update', 'tempo_traces_v2', toString(toUnixTimestamp(NOW())), NOW()); diff --git a/ctrl/qryn/sql/traces_dist.sql b/ctrl/qryn/sql/traces_dist.sql new file mode 100644 index 00000000..61fded35 --- /dev/null +++ b/ctrl/qryn/sql/traces_dist.sql @@ -0,0 +1,37 @@ +## These are comments +## The file for traces is Distributed tables +## Queries are separated with ";" and one empty string +## APPEND ONLY!!!!! +## Templating tokens: see log.sql + +CREATE TABLE IF NOT EXISTS {{.DB}}.tempo_traces_kv_dist {{.OnCluster}} ( + oid String, + date Date, + key String, + val_id String, + val String +) ENGINE = Distributed('{{.CLUSTER}}','{{.DB}}', 'tempo_traces_kv', sipHash64(oid, key)) {{.DIST_CREATE_SETTINGS}}; + +CREATE TABLE IF NOT EXISTS {{.DB}}.tempo_traces_dist {{.OnCluster}} ( + oid String, + trace_id FixedString(16), + span_id FixedString(8), + parent_id String, + name String, + timestamp_ns Int64 CODEC(DoubleDelta), + duration_ns Int64, + service_name String, + payload_type Int8, + payload String +) ENGINE = Distributed('{{.CLUSTER}}','{{.DB}}', 'tempo_traces', sipHash64(oid, trace_id)) {{.DIST_CREATE_SETTINGS}}; + +CREATE TABLE IF NOT EXISTS {{.DB}}.tempo_traces_attrs_gin_dist {{.OnCluster}} ( + oid String, + date Date, + key String, + val String, + trace_id FixedString(16), + span_id FixedString(8), + timestamp_ns Int64, + duration Int64 +) ENGINE = Distributed('{{.CLUSTER}}','{{.DB}}', 'tempo_traces_attrs_gin', sipHash64(oid, trace_id)) {{.DIST_CREATE_SETTINGS}}; \ No newline at end of file diff --git a/docker/docker-compose-centos.yml b/docker/docker-compose-centos.yml deleted file mode 100644 index 9beb607f..00000000 --- a/docker/docker-compose-centos.yml +++ /dev/null @@ -1,42 +0,0 @@ -# setup for centos -version: '2.1' - -volumes: - prometheus_data: {} - grafana_data: {} - -services: - grafana: - image: grafana/grafana:master - container_name: grafana - volumes: - - grafana_data:/var/lib/grafana - - ./grafana/provisioning/:/etc/grafana/provisioning/ - # - ./grafana/grafana.ini:/etc/grafana/grafana.ini - environment: - - GF_SECURITY_ADMIN_USER=${ADMIN_USER:-admin} - - GF_SECURITY_ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin} - - GF_USERS_ALLOW_SIGN_UP=false - - GF_EXPLORE_ENABLED=true - restart: unless-stopped - expose: - - 3000 - ports: - - 3000:3000 - labels: - org.label-schema.group: "monitoring" - - clickhouse-seed: - image: clickhouse/clickhouse-server - container_name: clickhouse-seed - ports: - - 8123:8123 - labels: - org.label-schema.group: "backend" - - centos: - image: centos/nodejs-12-centos7 - container_name: centos - volumes: - - ../:/opt/qryn - entrypoint: bash -c 'cd ~ ; cp -rf /opt/qryn . ; cd qryn; ls -la ; rm -rf node_modules ; npm install ; CLICKHOUSE_DB=loki CLICKHOUSE_TSDB=loki INTEGRATION_E2E=1 CLICKHOUSE_SERVER=clickhouse-seed node qryn.mjs' diff --git a/docker/docker-compose-s.yml b/docker/docker-compose-s.yml deleted file mode 100644 index 637f329f..00000000 --- a/docker/docker-compose-s.yml +++ /dev/null @@ -1,48 +0,0 @@ -# small setup for e2e tests -version: '2.1' - -volumes: - prometheus_data: {} - grafana_data: {} - -services: - alertman: - image: prom/alertmanager - container_name: alertman - volumes: - - ./alertmanager/config.yml:/etc/alertmanager/config.yml - entrypoint: alertmanager --config.file=/etc/alertmanager/config.yml - - grafana: - image: grafana/grafana:master - container_name: grafana - volumes: - - grafana_data:/var/lib/grafana - - ./grafana/provisioning/:/etc/grafana/provisioning/ - # - ./grafana/grafana.ini:/etc/grafana/grafana.ini - environment: - - GF_SECURITY_ADMIN_USER=${ADMIN_USER:-admin} - - GF_SECURITY_ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin} - - GF_USERS_ALLOW_SIGN_UP=false - - GF_EXPLORE_ENABLED=true - restart: unless-stopped - expose: - - 3000 - ports: - - 3000:3000 - labels: - org.label-schema.group: "monitoring" - - clickhouse-seed: - image: yandex/clickhouse-server - container_name: clickhouse-seed - ports: - - 8123:8123 - labels: - org.label-schema.group: "backend" - - loki: - image: grafana/loki - container_name: loki - ports: - - 3101:3100 diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml deleted file mode 100644 index 6050b889..00000000 --- a/docker/docker-compose.yml +++ /dev/null @@ -1,62 +0,0 @@ -version: '2.1' - -volumes: - grafana_data: {} - -services: - grafana: - image: grafana/grafana:master - container_name: grafana - volumes: - - grafana_data:/var/lib/grafana - - ./grafana/provisioning/:/etc/grafana/provisioning/ - environment: - - GF_SECURITY_ADMIN_USER=${ADMIN_USER:-admin} - - GF_SECURITY_ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin} - - GF_USERS_ALLOW_SIGN_UP=false - - GF_EXPLORE_ENABLED=true - restart: unless-stopped - expose: - - 3000 - - clickhouse-seed: - image: yandex/clickhouse-server - container_name: clickhouse-seed - ports: - - 8123:8123 - - clickhouse-client: - container_name: clickhouse-client - image: yandex/clickhouse-client - entrypoint: - - /bin/sleep - command: - - infinity - - otel-collector: - image: otel/opentelemetry-collector - command: ["--config=/etc/otel-collector-config.yaml"] - volumes: - - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml - ports: - - "4317:4317" # OTLP gRPC receiver - qryn: - image: qxip/qryn:latest - container_name: loki - restart: unless-stopped - expose: - - 3100 - ports: - - "3100:3100" - environment: - - CLICKHOUSE_SERVER=clickhouse-seed - - DEBUG=true - depends_on: - - clickhouse-seed - - pastash: - image: qxip/pastash-loki - container_name: pastash - volumes: - - ./loki/pastash.json:/config/loki.conf - - /var/log:/var/log:ro diff --git a/docker/e2e/docker-compose-cluster.yaml b/docker/e2e/docker-compose-cluster.yaml deleted file mode 100644 index c1526979..00000000 --- a/docker/e2e/docker-compose-cluster.yaml +++ /dev/null @@ -1,41 +0,0 @@ -version: '2.1' -networks: - common: - driver: bridge - -services: - clickhouse_1: - image: clickhouse/clickhouse-server:23.8 - container_name: clickhouse.cloki.deploy - hostname: clickhouse.cloki.deploy - domainname: clickhouse.cloki.deploy - restart: unless-stopped - networks: - common: - aliases: - - "clickhouse_1" - expose: - - "9000" - - "2181" - - "9444" - ports: - - 9000:9000 - - 8123:8123 - volumes: - - ./config.xml:/etc/clickhouse-server/config.xml - clickhouse_2: - image: clickhouse/clickhouse-server:23.8 - container_name: clickhouse2.cloki.deploy - hostname: clickhouse2.cloki.deploy - domainname: clickhouse2.cloki.deploy - restart: unless-stopped - networks: - common: - aliases: - - "clickhouse_2" - expose: - - "9000" - - "2181" - - "9444" - volumes: - - ./config2.xml:/etc/clickhouse-server/config.xml diff --git a/docker/grafana/grafana.ini b/docker/grafana/grafana.ini deleted file mode 100644 index 3d3ce2f7..00000000 --- a/docker/grafana/grafana.ini +++ /dev/null @@ -1,990 +0,0 @@ -##################### Grafana Configuration Example ##################### -# -# Everything has defaults so you only need to uncomment things you want to -# change - -# possible values : production, development -;app_mode = production - -# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty -;instance_name = ${HOSTNAME} - -#################################### Paths #################################### -[paths] -# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used) -;data = /var/lib/grafana - -# Temporary files in `data` directory older than given duration will be removed -;temp_data_lifetime = 24h - -# Directory where grafana can store logs -;logs = /var/log/grafana - -# Directory where grafana will automatically scan and look for plugins -;plugins = /var/lib/grafana/plugins - -# folder that contains provisioning config files that grafana will apply on startup and while running. -;provisioning = conf/provisioning - -#################################### Server #################################### -[server] -# Protocol (http, https, h2, socket) -;protocol = http - -# The ip address to bind to, empty will bind to all interfaces -;http_addr = - -# The http port to use -;http_port = 3000 - -# The public facing domain name used to access grafana from a browser -;domain = localhost - -# Redirect to correct domain if host header does not match domain -# Prevents DNS rebinding attacks -;enforce_domain = false - -# The full public facing url you use in browser, used for redirects and emails -# If you use reverse proxy and sub path specify full url (with sub path) -;root_url = %(protocol)s://%(domain)s:%(http_port)s/ - -# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons. -;serve_from_sub_path = false - -# Log web requests -;router_logging = false - -# the path relative working path -;static_root_path = public - -# enable gzip -;enable_gzip = false - -# https certs & key file -;cert_file = -;cert_key = - -# Unix socket path -;socket = - -# CDN Url -;cdn_url = - -# Sets the maximum time using a duration format (5s/5m/5ms) before timing out read of an incoming request and closing idle connections. -# `0` means there is no timeout for reading the request. -;read_timeout = 0 - -#################################### Database #################################### -[database] -# You can configure the database connection by specifying type, host, name, user and password -# as separate properties or as on string using the url properties. - -# Either "mysql", "postgres" or "sqlite3", it's your choice -;type = sqlite3 -;host = 127.0.0.1:3306 -;name = grafana -;user = root -# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;""" -;password = - -# Use either URL or the previous fields to configure the database -# Example: mysql://user:secret@host:port/database -;url = - -# For "postgres" only, either "disable", "require" or "verify-full" -;ssl_mode = disable - -# Database drivers may support different transaction isolation levels. -# Currently, only "mysql" driver supports isolation levels. -# If the value is empty - driver's default isolation level is applied. -# For "mysql" use "READ-UNCOMMITTED", "READ-COMMITTED", "REPEATABLE-READ" or "SERIALIZABLE". -;isolation_level = - -;ca_cert_path = -;client_key_path = -;client_cert_path = -;server_cert_name = - -# For "sqlite3" only, path relative to data_path setting -;path = grafana.db - -# Max idle conn setting default is 2 -;max_idle_conn = 2 - -# Max conn setting default is 0 (mean not set) -;max_open_conn = - -# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours) -;conn_max_lifetime = 14400 - -# Set to true to log the sql calls and execution times. -;log_queries = - -# For "sqlite3" only. cache mode setting used for connecting to the database. (private, shared) -;cache_mode = private - -################################### Data sources ######################### -[datasources] -# Upper limit of data sources that Grafana will return. This limit is a temporary configuration and it will be deprecated when pagination will be introduced on the list data sources API. -;datasource_limit = 5000 - -#################################### Cache server ############################# -[remote_cache] -# Either "redis", "memcached" or "database" default is "database" -;type = database - -# cache connectionstring options -# database: will use Grafana primary database. -# 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'. -# memcache: 127.0.0.1:11211 -;connstr = - -#################################### Data proxy ########################### -[dataproxy] - -# This enables data proxy logging, default is false -;logging = false - -# How long the data proxy waits to read the headers of the response before timing out, default is 30 seconds. -# This setting also applies to core backend HTTP data sources where query requests use an HTTP client with timeout set. -;timeout = 30 - -# How long the data proxy waits to establish a TCP connection before timing out, default is 10 seconds. -;dialTimeout = 10 - -# How many seconds the data proxy waits before sending a keepalive probe request. -;keep_alive_seconds = 30 - -# How many seconds the data proxy waits for a successful TLS Handshake before timing out. -;tls_handshake_timeout_seconds = 10 - -# How many seconds the data proxy will wait for a server's first response headers after -# fully writing the request headers if the request has an "Expect: 100-continue" -# header. A value of 0 will result in the body being sent immediately, without -# waiting for the server to approve. -;expect_continue_timeout_seconds = 1 - -# Optionally limits the total number of connections per host, including connections in the dialing, -# active, and idle states. On limit violation, dials will block. -# A value of zero (0) means no limit. -;max_conns_per_host = 0 - -# The maximum number of idle connections that Grafana will keep alive. -;max_idle_connections = 100 - -# How many seconds the data proxy keeps an idle connection open before timing out. -;idle_conn_timeout_seconds = 90 - -# If enabled and user is not anonymous, data proxy will add X-Grafana-User header with username into the request, default is false. -;send_user_header = false - -#################################### Analytics #################################### -[analytics] -# Server reporting, sends usage counters to stats.grafana.org every 24 hours. -# No ip addresses are being tracked, only simple counters to track -# running instances, dashboard and error counts. It is very helpful to us. -# Change this option to false to disable reporting. -;reporting_enabled = true - -# The name of the distributor of the Grafana instance. Ex hosted-grafana, grafana-labs -;reporting_distributor = grafana-labs - -# Set to false to disable all checks to https://grafana.net -# for new versions (grafana itself and plugins), check is used -# in some UI views to notify that grafana or plugin update exists -# This option does not cause any auto updates, nor send any information -# only a GET request to http://grafana.com to get latest versions -;check_for_updates = true - -# Google Analytics universal tracking code, only enabled if you specify an id here -;google_analytics_ua_id = - -# Google Tag Manager ID, only enabled if you specify an id here -;google_tag_manager_id = - -#################################### Security #################################### -[security] -# disable creation of admin user on first start of grafana -;disable_initial_admin_creation = false - -# default admin user, created on startup -;admin_user = admin - -# default admin password, can be changed before first start of grafana, or in profile settings -;admin_password = admin - -# used for signing -;secret_key = SW2YcwTIb9zpOOhoPsMm - -# disable gravatar profile images -;disable_gravatar = false - -# data source proxy whitelist (ip_or_domain:port separated by spaces) -;data_source_proxy_whitelist = - -# disable protection against brute force login attempts -;disable_brute_force_login_protection = false - -# set to true if you host Grafana behind HTTPS. default is false. -;cookie_secure = false - -# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict", "none" and "disabled" -;cookie_samesite = lax - -# set to true if you want to allow browsers to render Grafana in a <frame>, <iframe>, <embed> or <object>. default is false. -;allow_embedding = false - -# Set to true if you want to enable http strict transport security (HSTS) response header. -# This is only sent when HTTPS is enabled in this configuration. -# HSTS tells browsers that the site should only be accessed using HTTPS. -;strict_transport_security = false - -# Sets how long a browser should cache HSTS. Only applied if strict_transport_security is enabled. -;strict_transport_security_max_age_seconds = 86400 - -# Set to true if to enable HSTS preloading option. Only applied if strict_transport_security is enabled. -;strict_transport_security_preload = false - -# Set to true if to enable the HSTS includeSubDomains option. Only applied if strict_transport_security is enabled. -;strict_transport_security_subdomains = false - -# Set to true to enable the X-Content-Type-Options response header. -# The X-Content-Type-Options response HTTP header is a marker used by the server to indicate that the MIME types advertised -# in the Content-Type headers should not be changed and be followed. -;x_content_type_options = true - -# Set to true to enable the X-XSS-Protection header, which tells browsers to stop pages from loading -# when they detect reflected cross-site scripting (XSS) attacks. -;x_xss_protection = true - -# Enable adding the Content-Security-Policy header to your requests. -# CSP allows to control resources the user agent is allowed to load and helps prevent XSS attacks. -;content_security_policy = false - -# Set Content Security Policy template used when adding the Content-Security-Policy header to your requests. -# $NONCE in the template includes a random nonce. -# $ROOT_PATH is server.root_url without the protocol. -;content_security_policy_template = """script-src 'self' 'unsafe-eval' 'unsafe-inline' 'strict-dynamic' $NONCE;object-src 'none';font-src 'self';style-src 'self' 'unsafe-inline' blob:;img-src * data:;base-uri 'self';connect-src 'self' grafana.com ws://$ROOT_PATH wss://$ROOT_PATH;manifest-src 'self';media-src 'none';form-action 'self';""" - -#################################### Snapshots ########################### -[snapshots] -# snapshot sharing options -;external_enabled = true -;external_snapshot_url = https://snapshots-origin.raintank.io -;external_snapshot_name = Publish to snapshot.raintank.io - -# Set to true to enable this Grafana instance act as an external snapshot server and allow unauthenticated requests for -# creating and deleting snapshots. -;public_mode = false - -# remove expired snapshot -;snapshot_remove_expired = true - -#################################### Dashboards History ################## -[dashboards] -# Number dashboard versions to keep (per dashboard). Default: 20, Minimum: 1 -;versions_to_keep = 20 - -# Minimum dashboard refresh interval. When set, this will restrict users to set the refresh interval of a dashboard lower than given interval. Per default this is 5 seconds. -# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m. -;min_refresh_interval = 5s - -# Path to the default home dashboard. If this value is empty, then Grafana uses StaticRootPath + "dashboards/home.json" -;default_home_dashboard_path = - -#################################### Users ############################### -[users] -# disable user signup / registration -;allow_sign_up = true - -# Allow non admin users to create organizations -;allow_org_create = true - -# Set to true to automatically assign new users to the default organization (id 1) -;auto_assign_org = true - -# Set this value to automatically add new users to the provided organization (if auto_assign_org above is set to true) -;auto_assign_org_id = 1 - -# Default role new users will be automatically assigned (if disabled above is set to true) -;auto_assign_org_role = Viewer - -# Require email validation before sign up completes -;verify_email_enabled = false - -# Background text for the user field on the login page -;login_hint = email or username -;password_hint = password - -# Default UI theme ("dark" or "light") -;default_theme = dark - -# Path to a custom home page. Users are only redirected to this if the default home dashboard is used. It should match a frontend route and contain a leading slash. -; home_page = - -# External user management, these options affect the organization users view -;external_manage_link_url = -;external_manage_link_name = -;external_manage_info = - -# Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard. -;viewers_can_edit = false - -# Editors can administrate dashboard, folders and teams they create -;editors_can_admin = false - -# The duration in time a user invitation remains valid before expiring. This setting should be expressed as a duration. Examples: 6h (hours), 2d (days), 1w (week). Default is 24h (24 hours). The minimum supported duration is 15m (15 minutes). -;user_invite_max_lifetime_duration = 24h - -# Enter a comma-separated list of users login to hide them in the Grafana UI. These users are shown to Grafana admins and themselves. -; hidden_users = - -[auth] -# Login cookie name -;login_cookie_name = grafana_session - -# The maximum lifetime (duration) an authenticated user can be inactive before being required to login at next visit. Default is 7 days (7d). This setting should be expressed as a duration, e.g. 5m (minutes), 6h (hours), 10d (days), 2w (weeks), 1M (month). The lifetime resets at each successful token rotation. -;login_maximum_inactive_lifetime_duration = - -# The maximum lifetime (duration) an authenticated user can be logged in since login time before being required to login. Default is 30 days (30d). This setting should be expressed as a duration, e.g. 5m (minutes), 6h (hours), 10d (days), 2w (weeks), 1M (month). -;login_maximum_lifetime_duration = - -# How often should auth tokens be rotated for authenticated users when being active. The default is each 10 minutes. -;token_rotation_interval_minutes = 10 - -# Set to true to disable (hide) the login form, useful if you use OAuth, defaults to false -;disable_login_form = false - -# Set to true to disable the sign out link in the side menu. Useful if you use auth.proxy or auth.jwt, defaults to false -;disable_signout_menu = false - -# URL to redirect the user to after sign out -;signout_redirect_url = - -# Set to true to attempt login with OAuth automatically, skipping the login screen. -# This setting is ignored if multiple OAuth providers are configured. -;oauth_auto_login = false - -# OAuth state max age cookie duration in seconds. Defaults to 600 seconds. -;oauth_state_cookie_max_age = 600 - -# limit of api_key seconds to live before expiration -;api_key_max_seconds_to_live = -1 - -# Set to true to enable SigV4 authentication option for HTTP-based datasources. -;sigv4_auth_enabled = false - -#################################### Anonymous Auth ###################### -[auth.anonymous] -# enable anonymous access -;enabled = false - -# specify organization name that should be used for unauthenticated users -;org_name = Main Org. - -# specify role for unauthenticated users -;org_role = Viewer - -# mask the Grafana version number for unauthenticated users -;hide_version = false - -#################################### GitHub Auth ########################## -[auth.github] -;enabled = false -;allow_sign_up = true -;client_id = some_id -;client_secret = some_secret -;scopes = user:email,read:org -;auth_url = https://github.com/login/oauth/authorize -;token_url = https://github.com/login/oauth/access_token -;api_url = https://api.github.com/user -;allowed_domains = -;team_ids = -;allowed_organizations = - -#################################### GitLab Auth ######################### -[auth.gitlab] -;enabled = false -;allow_sign_up = true -;client_id = some_id -;client_secret = some_secret -;scopes = api -;auth_url = https://gitlab.com/oauth/authorize -;token_url = https://gitlab.com/oauth/token -;api_url = https://gitlab.com/api/v4 -;allowed_domains = -;allowed_groups = - -#################################### Google Auth ########################## -[auth.google] -;enabled = false -;allow_sign_up = true -;client_id = some_client_id -;client_secret = some_client_secret -;scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email -;auth_url = https://accounts.google.com/o/oauth2/auth -;token_url = https://accounts.google.com/o/oauth2/token -;api_url = https://www.googleapis.com/oauth2/v1/userinfo -;allowed_domains = -;hosted_domain = - -#################################### Grafana.com Auth #################### -[auth.grafana_com] -;enabled = false -;allow_sign_up = true -;client_id = some_id -;client_secret = some_secret -;scopes = user:email -;allowed_organizations = - -#################################### Azure AD OAuth ####################### -[auth.azuread] -;name = Azure AD -;enabled = false -;allow_sign_up = true -;client_id = some_client_id -;client_secret = some_client_secret -;scopes = openid email profile -;auth_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize -;token_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token -;allowed_domains = -;allowed_groups = - -#################################### Okta OAuth ####################### -[auth.okta] -;name = Okta -;enabled = false -;allow_sign_up = true -;client_id = some_id -;client_secret = some_secret -;scopes = openid profile email groups -;auth_url = https://<tenant-id>.okta.com/oauth2/v1/authorize -;token_url = https://<tenant-id>.okta.com/oauth2/v1/token -;api_url = https://<tenant-id>.okta.com/oauth2/v1/userinfo -;allowed_domains = -;allowed_groups = -;role_attribute_path = -;role_attribute_strict = false - -#################################### Generic OAuth ########################## -[auth.generic_oauth] -;enabled = false -;name = OAuth -;allow_sign_up = true -;client_id = some_id -;client_secret = some_secret -;scopes = user:email,read:org -;empty_scopes = false -;email_attribute_name = email:primary -;email_attribute_path = -;login_attribute_path = -;name_attribute_path = -;id_token_attribute_name = -;auth_url = https://foo.bar/login/oauth/authorize -;token_url = https://foo.bar/login/oauth/access_token -;api_url = https://foo.bar/user -;allowed_domains = -;team_ids = -;allowed_organizations = -;role_attribute_path = -;role_attribute_strict = false -;groups_attribute_path = -;tls_skip_verify_insecure = false -;tls_client_cert = -;tls_client_key = -;tls_client_ca = - -#################################### Basic Auth ########################## -[auth.basic] -;enabled = true - -#################################### Auth Proxy ########################## -[auth.proxy] -;enabled = false -;header_name = X-WEBAUTH-USER -;header_property = username -;auto_sign_up = true -;sync_ttl = 60 -;whitelist = 192.168.1.1, 192.168.2.1 -;headers = Email:X-User-Email, Name:X-User-Name -# Read the auth proxy docs for details on what the setting below enables -;enable_login_token = false - -#################################### Auth JWT ########################## -[auth.jwt] -;enabled = true -;header_name = X-JWT-Assertion -;email_claim = sub -;username_claim = sub -;jwk_set_url = https://foo.bar/.well-known/jwks.json -;jwk_set_file = /path/to/jwks.json -;cache_ttl = 60m -;expected_claims = {"aud": ["foo", "bar"]} -;key_file = /path/to/key/file - -#################################### Auth LDAP ########################## -[auth.ldap] -;enabled = false -;config_file = /etc/grafana/ldap.toml -;allow_sign_up = true - -# LDAP background sync (Enterprise only) -# At 1 am every day -;sync_cron = "0 0 1 * * *" -;active_sync_enabled = true - -#################################### AWS ########################### -[aws] -# Enter a comma-separated list of allowed AWS authentication providers. -# Options are: default (AWS SDK Default), keys (Access && secret key), credentials (Credentials field), ec2_iam_role (EC2 IAM Role) -; allowed_auth_providers = default,keys,credentials - -# Allow AWS users to assume a role using temporary security credentials. -# If true, assume role will be enabled for all AWS authentication providers that are specified in aws_auth_providers -; assume_role_enabled = true - -#################################### Azure ############################### -[azure] -# Azure cloud environment where Grafana is hosted -# Possible values are AzureCloud, AzureChinaCloud, AzureUSGovernment and AzureGermanCloud -# Default value is AzureCloud (i.e. public cloud) -;cloud = AzureCloud - -# Specifies whether Grafana hosted in Azure service with Managed Identity configured (e.g. Azure Virtual Machines instance) -# If enabled, the managed identity can be used for authentication of Grafana in Azure services -# Disabled by default, needs to be explicitly enabled -;managed_identity_enabled = false - -# Client ID to use for user-assigned managed identity -# Should be set for user-assigned identity and should be empty for system-assigned identity -;managed_identity_client_id = - -#################################### SMTP / Emailing ########################## -[smtp] -enabled = true -host = smtp.gmail.com:465 -user = akvlad90@gmail.com -;cert_file = -;key_file = -;skip_verify = false -;from_address = admin@grafana.localhost -;from_name = Grafana -# EHLO identity in SMTP dialog (defaults to instance_name) -;ehlo_identity = dashboard.example.com -# SMTP startTLS policy (defaults to 'OpportunisticStartTLS') -;startTLS_policy = NoStartTLS - -[emails] -;welcome_email_on_sign_up = false -;templates_pattern = emails/*.html - -#################################### Logging ########################## -[log] -# Either "console", "file", "syslog". Default is console and file -# Use space to separate multiple modes, e.g. "console file" -;mode = console file - -# Either "debug", "info", "warn", "error", "critical", default is "info" -;level = info - -# optional settings to set different levels for specific loggers. Ex filters = sqlstore:debug -;filters = - -# For "console" mode only -[log.console] -;level = - -# log line format, valid options are text, console and json -;format = console - -# For "file" mode only -[log.file] -;level = - -# log line format, valid options are text, console and json -;format = text - -# This enables automated log rotate(switch of following options), default is true -;log_rotate = true - -# Max line number of single file, default is 1000000 -;max_lines = 1000000 - -# Max size shift of single file, default is 28 means 1 << 28, 256MB -;max_size_shift = 28 - -# Segment log daily, default is true -;daily_rotate = true - -# Expired days of log file(delete after max days), default is 7 -;max_days = 7 - -[log.syslog] -;level = - -# log line format, valid options are text, console and json -;format = text - -# Syslog network type and address. This can be udp, tcp, or unix. If left blank, the default unix endpoints will be used. -;network = -;address = - -# Syslog facility. user, daemon and local0 through local7 are valid. -;facility = - -# Syslog tag. By default, the process' argv[0] is used. -;tag = - -[log.frontend] -# Should Sentry javascript agent be initialized -;enabled = false - -# Sentry DSN if you want to send events to Sentry. -;sentry_dsn = - -# Custom HTTP endpoint to send events captured by the Sentry agent to. Default will log the events to stdout. -;custom_endpoint = /log - -# Rate of events to be reported between 0 (none) and 1 (all), float -;sample_rate = 1.0 - -# Requests per second limit enforced an extended period, for Grafana backend log ingestion endpoint (/log). -;log_endpoint_requests_per_second_limit = 3 - -# Max requests accepted per short interval of time for Grafana backend log ingestion endpoint (/log). -;log_endpoint_burst_limit = 15 - -#################################### Usage Quotas ######################## -[quota] -; enabled = false - -#### set quotas to -1 to make unlimited. #### -# limit number of users per Org. -; org_user = 10 - -# limit number of dashboards per Org. -; org_dashboard = 100 - -# limit number of data_sources per Org. -; org_data_source = 10 - -# limit number of api_keys per Org. -; org_api_key = 10 - -# limit number of alerts per Org. -;org_alert_rule = 100 - -# limit number of orgs a user can create. -; user_org = 10 - -# Global limit of users. -; global_user = -1 - -# global limit of orgs. -; global_org = -1 - -# global limit of dashboards -; global_dashboard = -1 - -# global limit of api_keys -; global_api_key = -1 - -# global limit on number of logged in users. -; global_session = -1 - -# global limit of alerts -;global_alert_rule = -1 - -#################################### Alerting ############################ -[alerting] -# Disable alerting engine & UI features -;enabled = true -# Makes it possible to turn off alert rule execution but alerting UI is visible -;execute_alerts = true - -# Default setting for new alert rules. Defaults to categorize error and timeouts as alerting. (alerting, keep_state) -;error_or_timeout = alerting - -# Default setting for how Grafana handles nodata or null values in alerting. (alerting, no_data, keep_state, ok) -;nodata_or_nullvalues = no_data - -# Alert notifications can include images, but rendering many images at the same time can overload the server -# This limit will protect the server from render overloading and make sure notifications are sent out quickly -;concurrent_render_limit = 5 - - -# Default setting for alert calculation timeout. Default value is 30 -;evaluation_timeout_seconds = 30 - -# Default setting for alert notification timeout. Default value is 30 -;notification_timeout_seconds = 30 - -# Default setting for max attempts to sending alert notifications. Default value is 3 -;max_attempts = 3 - -# Makes it possible to enforce a minimal interval between evaluations, to reduce load on the backend -;min_interval_seconds = 1 - -# Configures for how long alert annotations are stored. Default is 0, which keeps them forever. -# This setting should be expressed as a duration. Examples: 6h (hours), 10d (days), 2w (weeks), 1M (month). -;max_annotation_age = - -# Configures max number of alert annotations that Grafana stores. Default value is 0, which keeps all alert annotations. -;max_annotations_to_keep = - -#################################### Annotations ######################### -[annotations] -# Configures the batch size for the annotation clean-up job. This setting is used for dashboard, API, and alert annotations. -;cleanupjob_batchsize = 100 - -[annotations.dashboard] -# Dashboard annotations means that annotations are associated with the dashboard they are created on. - -# Configures how long dashboard annotations are stored. Default is 0, which keeps them forever. -# This setting should be expressed as a duration. Examples: 6h (hours), 10d (days), 2w (weeks), 1M (month). -;max_age = - -# Configures max number of dashboard annotations that Grafana stores. Default value is 0, which keeps all dashboard annotations. -;max_annotations_to_keep = - -[annotations.api] -# API annotations means that the annotations have been created using the API without any -# association with a dashboard. - -# Configures how long Grafana stores API annotations. Default is 0, which keeps them forever. -# This setting should be expressed as a duration. Examples: 6h (hours), 10d (days), 2w (weeks), 1M (month). -;max_age = - -# Configures max number of API annotations that Grafana keeps. Default value is 0, which keeps all API annotations. -;max_annotations_to_keep = - -#################################### Explore ############################# -[explore] -# Enable the Explore section -;enabled = true - -#################################### Internal Grafana Metrics ########################## -# Metrics available at HTTP API Url /metrics -[metrics] -# Disable / Enable internal metrics -;enabled = true -# Graphite Publish interval -;interval_seconds = 10 -# Disable total stats (stat_totals_*) metrics to be generated -;disable_total_stats = false - -#If both are set, basic auth will be required for the metrics endpoint. -; basic_auth_username = -; basic_auth_password = - -# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which -# can expose more information about the Grafana instance. -[metrics.environment_info] -#exampleLabel1 = exampleValue1 -#exampleLabel2 = exampleValue2 - -# Send internal metrics to Graphite -[metrics.graphite] -# Enable by setting the address setting (ex localhost:2003) -;address = -;prefix = prod.grafana.%(instance_name)s. - -#################################### Grafana.com integration ########################## -# Url used to import dashboards directly from Grafana.com -[grafana_com] -;url = https://grafana.com - -#################################### Distributed tracing ############ -[tracing.jaeger] -# Enable by setting the address sending traces to jaeger (ex localhost:6831) -;address = localhost:6831 -# Tag that will always be included in when creating new spans. ex (tag1:value1,tag2:value2) -;always_included_tag = tag1:value1 -# Type specifies the type of the sampler: const, probabilistic, rateLimiting, or remote -;sampler_type = const -# jaeger samplerconfig param -# for "const" sampler, 0 or 1 for always false/true respectively -# for "probabilistic" sampler, a probability between 0 and 1 -# for "rateLimiting" sampler, the number of spans per second -# for "remote" sampler, param is the same as for "probabilistic" -# and indicates the initial sampling rate before the actual one -# is received from the mothership -;sampler_param = 1 -# sampling_server_url is the URL of a sampling manager providing a sampling strategy. -;sampling_server_url = -# Whether or not to use Zipkin propagation (x-b3- HTTP headers). -;zipkin_propagation = false -# Setting this to true disables shared RPC spans. -# Not disabling is the most common setting when using Zipkin elsewhere in your infrastructure. -;disable_shared_zipkin_spans = false - -#################################### External image storage ########################## -[external_image_storage] -# Used for uploading images to public servers so they can be included in slack/email messages. -# you can choose between (s3, webdav, gcs, azure_blob, local) -;provider = - -[external_image_storage.s3] -;endpoint = -;path_style_access = -;bucket = -;region = -;path = -;access_key = -;secret_key = - -[external_image_storage.webdav] -;url = -;public_url = -;username = -;password = - -[external_image_storage.gcs] -;key_file = -;bucket = -;path = - -[external_image_storage.azure_blob] -;account_name = -;account_key = -;container_name = - -[external_image_storage.local] -# does not require any configuration - -[rendering] -# Options to configure a remote HTTP image rendering service, e.g. using https://github.com/grafana/grafana-image-renderer. -# URL to a remote HTTP image renderer service, e.g. http://localhost:8081/render, will enable Grafana to render panels and dashboards to PNG-images using HTTP requests to an external service. -;server_url = -# If the remote HTTP image renderer service runs on a different server than the Grafana server you may have to configure this to a URL where Grafana is reachable, e.g. http://grafana.domain/. -;callback_url = -# Concurrent render request limit affects when the /render HTTP endpoint is used. Rendering many images at the same time can overload the server, -# which this setting can help protect against by only allowing a certain amount of concurrent requests. -;concurrent_render_request_limit = 30 - -[panels] -# If set to true Grafana will allow script tags in text panels. Not recommended as it enable XSS vulnerabilities. -;disable_sanitize_html = false - -[plugins] -;enable_alpha = false -;app_tls_skip_verify_insecure = false -# Enter a comma-separated list of plugin identifiers to identify plugins to load even if they are unsigned. Plugins with modified signatures are never loaded. -;allow_loading_unsigned_plugins = -# Enable or disable installing plugins directly from within Grafana. -;plugin_admin_enabled = false -;plugin_admin_external_manage_enabled = false -;plugin_catalog_url = https://grafana.com/grafana/plugins/ - -#################################### Grafana Live ########################################## -[live] -# max_connections to Grafana Live WebSocket endpoint per Grafana server instance. See Grafana Live docs -# if you are planning to make it higher than default 100 since this can require some OS and infrastructure -# tuning. 0 disables Live, -1 means unlimited connections. -;max_connections = 100 - -# allowed_origins is a comma-separated list of origins that can establish connection with Grafana Live. -# If not set then origin will be matched over root_url. Supports wildcard symbol "*". -;allowed_origins = - -# engine defines an HA (high availability) engine to use for Grafana Live. By default no engine used - in -# this case Live features work only on a single Grafana server. Available options: "redis". -# Setting ha_engine is an EXPERIMENTAL feature. -;ha_engine = - -# ha_engine_address sets a connection address for Live HA engine. Depending on engine type address format can differ. -# For now we only support Redis connection address in "host:port" format. -# This option is EXPERIMENTAL. -;ha_engine_address = "127.0.0.1:6379" - -#################################### Grafana Image Renderer Plugin ########################## -[plugin.grafana-image-renderer] -# Instruct headless browser instance to use a default timezone when not provided by Grafana, e.g. when rendering panel image of alert. -# See ICU’s metaZones.txt (https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt) for a list of supported -# timezone IDs. Fallbacks to TZ environment variable if not set. -;rendering_timezone = - -# Instruct headless browser instance to use a default language when not provided by Grafana, e.g. when rendering panel image of alert. -# Please refer to the HTTP header Accept-Language to understand how to format this value, e.g. 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5'. -;rendering_language = - -# Instruct headless browser instance to use a default device scale factor when not provided by Grafana, e.g. when rendering panel image of alert. -# Default is 1. Using a higher value will produce more detailed images (higher DPI), but will require more disk space to store an image. -;rendering_viewport_device_scale_factor = - -# Instruct headless browser instance whether to ignore HTTPS errors during navigation. Per default HTTPS errors are not ignored. Due to -# the security risk it's not recommended to ignore HTTPS errors. -;rendering_ignore_https_errors = - -# Instruct headless browser instance whether to capture and log verbose information when rendering an image. Default is false and will -# only capture and log error messages. When enabled, debug messages are captured and logged as well. -# For the verbose information to be included in the Grafana server log you have to adjust the rendering log level to debug, configure -# [log].filter = rendering:debug. -;rendering_verbose_logging = - -# Instruct headless browser instance whether to output its debug and error messages into running process of remote rendering service. -# Default is false. This can be useful to enable (true) when troubleshooting. -;rendering_dumpio = - -# Additional arguments to pass to the headless browser instance. Default is --no-sandbox. The list of Chromium flags can be found -# here (https://peter.sh/experiments/chromium-command-line-switches/). Multiple arguments is separated with comma-character. -;rendering_args = - -# You can configure the plugin to use a different browser binary instead of the pre-packaged version of Chromium. -# Please note that this is not recommended, since you may encounter problems if the installed version of Chrome/Chromium is not -# compatible with the plugin. -;rendering_chrome_bin = - -# Instruct how headless browser instances are created. Default is 'default' and will create a new browser instance on each request. -# Mode 'clustered' will make sure that only a maximum of browsers/incognito pages can execute concurrently. -# Mode 'reusable' will have one browser instance and will create a new incognito page on each request. -;rendering_mode = - -# When rendering_mode = clustered you can instruct how many browsers or incognito pages can execute concurrently. Default is 'browser' -# and will cluster using browser instances. -# Mode 'context' will cluster using incognito pages. -;rendering_clustering_mode = -# When rendering_mode = clustered you can define maximum number of browser instances/incognito pages that can execute concurrently.. -;rendering_clustering_max_concurrency = - -# Limit the maximum viewport width, height and device scale factor that can be requested. -;rendering_viewport_max_width = -;rendering_viewport_max_height = -;rendering_viewport_max_device_scale_factor = - -# Change the listening host and port of the gRPC server. Default host is 127.0.0.1 and default port is 0 and will automatically assign -# a port not in use. -;grpc_host = -;grpc_port = - -[enterprise] -# Path to a valid Grafana Enterprise license.jwt file -;license_path = - -[feature_toggles] -# enable features, separated by spaces -;enable = - -[date_formats] -# For information on what formatting patterns that are supported https://momentjs.com/docs/#/displaying/ - -# Default system date format used in time range picker and other places where full time is displayed -;full_date = YYYY-MM-DD HH:mm:ss - -# Used by graph and other places where we only show small intervals -;interval_second = HH:mm:ss -;interval_minute = HH:mm -;interval_hour = MM/DD HH:mm -;interval_day = MM/DD -;interval_month = YYYY-MM -;interval_year = YYYY - -# Experimental feature -;use_browser_locale = false - -# Default timezone for user preferences. Options are 'browser' for the browser local timezone or a timezone name from IANA Time Zone database, e.g. 'UTC' or 'Europe/Amsterdam' etc. -;default_timezone = browser - -[expressions] -# Enable or disable the expressions functionality. -;enabled = true \ No newline at end of file diff --git a/docker/grafana/provisioning/dashboards/dashboard.yml b/docker/grafana/provisioning/dashboards/dashboard.yml deleted file mode 100644 index 14716ee1..00000000 --- a/docker/grafana/provisioning/dashboards/dashboard.yml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: 1 - -providers: -- name: 'Prometheus' - orgId: 1 - folder: '' - type: file - disableDeletion: false - editable: true - options: - path: /etc/grafana/provisioning/dashboards diff --git a/docker/grafana/provisioning/datasources/datasource.yml b/docker/grafana/provisioning/datasources/datasource.yml deleted file mode 100644 index c3986f86..00000000 --- a/docker/grafana/provisioning/datasources/datasource.yml +++ /dev/null @@ -1,13 +0,0 @@ -# config file version -apiVersion: 1 - -deleteDatasources: - - name: Loki - orgId: 1 - -datasources: -- name: Loki - type: loki - access: proxy - url: http://loki:3100 - editable: true diff --git a/docker/loki/pastash.conf b/docker/loki/pastash.conf deleted file mode 100644 index ac906c8a..00000000 --- a/docker/loki/pastash.conf +++ /dev/null @@ -1,13 +0,0 @@ -input { - file { - path => "/var/log/*.log" - } -} - -output { - loki { - host => loki - port => 3100 - path => "/loki/api/v1/push" - } -} diff --git a/docker/loki/promtail-docker-config.yaml b/docker/loki/promtail-docker-config.yaml deleted file mode 100644 index 9375388b..00000000 --- a/docker/loki/promtail-docker-config.yaml +++ /dev/null @@ -1,19 +0,0 @@ -server: - http_listen_port: 0 - grpc_listen_port: 0 - -positions: - filename: /tmp/positions.yaml - -client: - url: http://loki:3100/loki/api/v1/push - -scrape_configs: -- job_name: system - entry_parser: raw - static_configs: - - targets: - - localhost - labels: - job: varlogs - __path__: /var/log/syslog diff --git a/docker/otel-collector-config.yaml b/docker/otel-collector-config.yaml deleted file mode 100644 index 780390a2..00000000 --- a/docker/otel-collector-config.yaml +++ /dev/null @@ -1,14 +0,0 @@ -receivers: - otlp: - protocols: - grpc: -exporters: - otlphttp/qryn: - endpoint: http://qryn:3100 - tls: - insecure: true -service: - pipelines: - traces: - receivers: [otlp] - exporters: [otlphttp/qryn] diff --git a/docker/testmetrics.sh b/docker/testmetrics.sh deleted file mode 100755 index ceeae411..00000000 --- a/docker/testmetrics.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -## Test Metrics for Loki/Qryn - - DOMAINS=("up" "down" "left" "right") - - for i in `seq 1 10`; - do - TIME=$(date --utc +%FT%T.%3NZ) - RANDOM=$$$(date +%s) - NAME=${DOMAINS[$RANDOM % ${#DOMAINS[@]}]} - echo "$NAME, $TIME" - curl --header "Content-Type: application/json" --request POST \ - --data '{"streams": [{"labels": "{foo=\"bar\",name=\"'"$NAME"'\"}","entries": [{"ts": "'"$TIME"'", "line": "level=info string='"$RANDOM"'" }]}]}' \ - 'http://127.0.0.1:3100/loki/api/v1/push' & - done diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..1d20b0c6 --- /dev/null +++ b/go.mod @@ -0,0 +1,193 @@ +module github.com/metrico/qryn + +go 1.23.0 + +toolchain go1.23.2 + +replace ( + cloud.google.com/go/compute v0.2.0 => cloud.google.com/go/compute v1.7.0 + + github.com/docker/distribution v2.7.1+incompatible => github.com/docker/distribution v2.8.0+incompatible + github.com/pascaldekloe/mqtt v1.0.0 => github.com/metrico/mqtt v1.0.1-0.20220314083119-cb53cdb0fcbe + github.com/prometheus/prometheus v0.300.1 => github.com/prometheus/prometheus v1.8.2-0.20220714142409-b41e0750abf5 + //TODO: remove this + go.opentelemetry.io/collector/pdata v1.12.0 => go.opentelemetry.io/collector/pdata v0.62.1 + go.opentelemetry.io/otel v1.19.0 => go.opentelemetry.io/otel v1.7.0 + go.opentelemetry.io/otel/internal/global v1.19.0 => go.opentelemetry.io/otel/internal/global v1.7.0 + go.opentelemetry.io/otel/metric v1.21.0 => go.opentelemetry.io/otel/metric v0.30.0 + google.golang.org/grpc v1.47.0 => google.golang.org/grpc v1.45.0 + k8s.io/api v0.32.0 => k8s.io/api v0.24.17 + k8s.io/apimachinery v0.32.0 => k8s.io/apimachinery v0.24.17 + k8s.io/client-go v12.0.0+incompatible => k8s.io/client-go v0.22.1 + +) + +// + +require ( + github.com/ClickHouse/ch-go v0.61.5 + github.com/ClickHouse/clickhouse-go/v2 v2.30.0 + github.com/Masterminds/sprig v2.22.0+incompatible + github.com/VictoriaMetrics/fastcache v1.12.2 + github.com/alecthomas/participle/v2 v2.1.1 + github.com/avast/retry-go v3.0.0+incompatible + github.com/bradleyjkemp/cupaloy v2.3.0+incompatible + github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 + github.com/go-faster/city v1.0.1 + github.com/go-faster/jx v1.1.0 + github.com/go-kit/kit v0.10.0 + github.com/go-logfmt/logfmt v0.6.0 + github.com/gofiber/fiber/v2 v2.52.5 + github.com/gofiber/websocket/v2 v2.2.1 + github.com/golang/snappy v0.0.4 + github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 + github.com/gorilla/mux v1.8.1 + github.com/gorilla/schema v1.4.1 + github.com/gorilla/websocket v1.5.3 + github.com/grafana/pyroscope-go v1.2.0 + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc + github.com/influxdata/telegraf v1.33.0 + github.com/jmoiron/sqlx v1.4.0 + github.com/json-iterator/go v1.1.12 + github.com/kr/logfmt v0.0.0-20210122060352-19f9bcb100e6 + github.com/labstack/gommon v0.4.2 + github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible + github.com/m3db/prometheus_remote_client_golang v0.4.4 + github.com/metrico/cloki-config v0.0.82 + github.com/mochi-co/mqtt v1.3.2 + github.com/openzipkin/zipkin-go v0.4.3 + github.com/pkg/errors v0.9.1 + github.com/prometheus/client_golang v1.20.5 + github.com/prometheus/common v0.60.0 + github.com/prometheus/prometheus v1.8.2-0.20220714142409-b41e0750abf5 + github.com/sirupsen/logrus v1.9.3 + github.com/stretchr/testify v1.10.0 + github.com/valyala/bytebufferpool v1.0.0 + github.com/valyala/fasthttp v1.52.0 + github.com/valyala/fastjson v1.6.4 + go.opentelemetry.io/collector/pdata v1.12.0 + go.opentelemetry.io/proto/otlp v1.4.0 + golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 + golang.org/x/sync v0.10.0 + google.golang.org/grpc v1.68.1 + google.golang.org/protobuf v1.36.1 + gopkg.in/go-playground/validator.v9 v9.31.0 + gopkg.in/yaml.v2 v2.4.0 +) + +require ( + filippo.io/edwards25519 v1.1.0 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver v1.5.0 // indirect + github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 // indirect + github.com/andybalholm/brotli v1.1.1 // indirect + github.com/antlr4-go/antlr/v4 v4.13.1 // indirect + github.com/awnumar/memcall v0.3.0 // indirect + github.com/awnumar/memguard v0.22.5 // indirect + github.com/aws/aws-sdk-go v1.55.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/compose-spec/compose-go v1.20.2 // indirect + github.com/coreos/go-semver v0.3.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dennwc/varint v1.0.0 // indirect + github.com/dmarkham/enumer v1.5.9 // indirect + github.com/edsrzf/mmap-go v1.1.0 // indirect + github.com/fasthttp/websocket v1.5.3 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-faster/errors v0.7.1 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/gobwas/glob v0.2.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/cel-go v0.21.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/huandu/xstrings v1.5.0 // indirect + github.com/imdario/mergo v0.3.16 // indirect + github.com/influxdata/toml v0.0.0-20190415235208-270119a8ce65 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/jonboulle/clockwork v0.4.0 // indirect + github.com/jpillora/backoff v1.0.0 // indirect + github.com/julienschmidt/httprouter v1.3.0 // indirect + github.com/klauspost/compress v1.17.10 // indirect + github.com/klauspost/pgzip v1.2.6 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/lestrrat-go/strftime v1.1.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mcuadros/go-defaults v1.2.0 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect + github.com/naoina/go-stringutil v0.1.0 // indirect + github.com/oklog/ulid v1.3.1 // indirect + github.com/pascaldekloe/name v1.0.1 // indirect + github.com/paulmach/orb v0.11.1 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pierrec/lz4/v4 v4.1.21 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common/sigv4 v0.1.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rs/xid v1.5.0 // indirect + github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect + github.com/segmentio/asm v1.2.0 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.16.0 // indirect + github.com/stoewer/go-strcase v1.3.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/subosito/gotenv v1.4.2 // indirect + github.com/tidwall/gjson v1.18.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // indirect + github.com/tidwall/tinylru v1.2.1 // indirect + github.com/tidwall/wal v1.1.7 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect + go.opentelemetry.io/otel v1.31.0 // indirect + go.opentelemetry.io/otel/metric v1.31.0 // indirect + go.opentelemetry.io/otel/trace v1.31.0 // indirect + go.step.sm/crypto v0.54.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/goleak v1.3.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/crypto v0.31.0 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect + golang.org/x/time v0.7.0 // indirect + golang.org/x/tools v0.28.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.32.0 // indirect + k8s.io/apimachinery v0.32.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..a97103fd --- /dev/null +++ b/go.sum @@ -0,0 +1,1604 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= +cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo= +cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +collectd.org v0.6.0 h1:wDTcB13Zork7m9bEHmU2sVL4z+hxBmm8EyeMjjxtW7s= +collectd.org v0.6.0/go.mod h1:fXcRZb1qBKshIHJa2T8qBS7Xew/I43iMutefnTdGeYo= +contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeSEBLPA9YJp5bjrofdU3pIXs= +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/Azure/azure-sdk-for-go v23.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= +github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-autorest v11.2.8+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= +github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= +github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= +github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8= +github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= +github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeEI4= +github.com/ClickHouse/ch-go v0.61.5/go.mod h1:s1LJW/F/LcFs5HJnuogFMta50kKDO0lf9zzfrbl0RQg= +github.com/ClickHouse/clickhouse-go/v2 v2.30.0 h1:AG4D/hW39qa58+JHQIFOSnxyL46H6h2lrmGGk17dhFo= +github.com/ClickHouse/clickhouse-go/v2 v2.30.0/go.mod h1:i9ZQAojcayW3RsdCb3YR+n+wC2h65eJsZCscZ1Z1wyo= +github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= +github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= +github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= +github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= +github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/assert/v2 v2.3.0 h1:mAsH2wmvjsuvyBvAmCtm7zFsBlb8mIHx5ySLVdDZXL0= +github.com/alecthomas/assert/v2 v2.3.0/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8= +github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 h1:t3eaIm0rUkzbrIewtiFmMK5RXHej2XnoXNhxVsAYUfg= +github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= +github.com/alitto/pond v1.9.2 h1:9Qb75z/scEZVCoSU+osVmQ0I0JOeLfdTDafrbcJ8CLs= +github.com/alitto/pond v1.9.2/go.mod h1:xQn3P/sHTYcU/1BR3i86IGIrilcrGC2LiS+E2+CJWsI= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/amir/raidman v0.0.0-20170415203553-1ccc43bfb9c9 h1:FXrPTd8Rdlc94dKccl7KPmdmIbVh/OjelJ8/vgMRzcQ= +github.com/amir/raidman v0.0.0-20170415203553-1ccc43bfb9c9/go.mod h1:eliMa/PW+RDr2QLWRmLH1R1ZA4RInpmvOzDDXtaIZkc= +github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= +github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= +github.com/antchfx/jsonquery v1.3.3 h1:zjZpbnZhYng3uOAbIfdNq81A9mMEeuDJeYIpeKpZ4es= +github.com/antchfx/jsonquery v1.3.3/go.mod h1:1JG4DqRlRCHgVYDPY1ioYFAGSXGfWHzNgrbiGQHsWck= +github.com/antchfx/xmlquery v1.4.1 h1:YgpSwbeWvLp557YFTi8E3z6t6/hYjmFEtiEKbDfEbl0= +github.com/antchfx/xmlquery v1.4.1/go.mod h1:lKezcT8ELGt8kW5L+ckFMTbgdR61/odpPgDv8Gvi1fI= +github.com/antchfx/xpath v1.3.1 h1:PNbFuUqHwWl0xRjvUPjJ95Agbmdj2uzzIwmQKgu4oCk= +github.com/antchfx/xpath v1.3.1/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= +github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= +github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= +github.com/apache/arrow/go/v18 v18.0.0-20240716144821-cf5d7c7ec3cf h1:9b4bG4uqvid0RH3MHWq2soXTfhPFbqbuNCqLRrl4ZGg= +github.com/apache/arrow/go/v18 v18.0.0-20240716144821-cf5d7c7ec3cf/go.mod h1:84kVJOfdiXAj9Zo8lvZ2uuJVzPn2vKlPdrSHU1zD2mE= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE= +github.com/apache/thrift v0.21.0/go.mod h1:W1H8aR/QRtYNvrPeFXBtobyRkd0/YVhTc6i07XIAgDw= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= +github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= +github.com/awnumar/memcall v0.3.0 h1:8b/3Sptrtgejj2kLgL6M5F2r4OzTf19CTllO+gIXUg8= +github.com/awnumar/memcall v0.3.0/go.mod h1:8xOx1YbfyuCg3Fy6TO8DK0kZUua3V42/goA5Ru47E8w= +github.com/awnumar/memguard v0.22.5 h1:PH7sbUVERS5DdXh3+mLo8FDcl1eIeVjJVYMnyuYpvuI= +github.com/awnumar/memguard v0.22.5/go.mod h1:+APmZGThMBWjnMlKiSM1X7MVpbIVewen2MTkqWkA/zE= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.22.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/blues/jsonata-go v1.5.4 h1:XCsXaVVMrt4lcpKeJw6mNJHqQpWU751cnHdCFUq3xd8= +github.com/blues/jsonata-go v1.5.4/go.mod h1:uns2jymDrnI7y+UFYCqsRTEiAH22GyHnNXrkupAVFWI= +github.com/bradleyjkemp/cupaloy v2.3.0+incompatible h1:UafIjBvWQmS9i/xRg+CamMrnLTKNzo+bdmT/oH34c2Y= +github.com/bradleyjkemp/cupaloy v2.3.0+incompatible/go.mod h1:Au1Xw1sgaJ5iSFktEhYsS0dbQiS1B0/XMXl+42y9Ilk= +github.com/bufbuild/protocompile v0.10.0 h1:+jW/wnLMLxaCEG8AX9lD0bQ5v9h1RUiMKOBOT5ll9dM= +github.com/bufbuild/protocompile v0.10.0/go.mod h1:G9qQIQo0xZ6Uyj6CMNz0saGmx2so+KONo8/KrELABiY= +github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 h1:6lhrsTEnloDPXyeZBvSYvQf8u86jbKehZPVDDlkgDl4= +github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v0.0.0-20181003080854-62661b46c409/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v0.0.0-20181017004759-096ff4a8a059/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudevents/sdk-go/v2 v2.15.2 h1:54+I5xQEnI73RBhWHxbI1XJcqOFOVJN85vb41+8mHUc= +github.com/cloudevents/sdk-go/v2 v2.15.2/go.mod h1:lL7kSWAE/V8VI4Wh0jbL2v/jvqsm6tjmaQBSvxcv4uE= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI= +github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/compose-spec/compose-go v1.20.2 h1:u/yfZHn4EaHGdidrZycWpxXgFffjYULlTbRfJ51ykjQ= +github.com/compose-spec/compose-go v1.20.2/go.mod h1:+MdqXV4RA7wdFsahh/Kb8U0pAJqkg7mr4PM9tFKU8RM= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= +github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE= +github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/digitalocean/godo v1.81.0 h1:sjb3fOfPfSlUQUK22E87BcI8Zx2qtnF7VUCCO4UK3C8= +github.com/digitalocean/godo v1.81.0/go.mod h1:BPCqvwbjbGqxuUnIKB4EvS/AX7IDnNmt5fwvIkWo+ew= +github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= +github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/dmarkham/enumer v1.5.9 h1:NM/1ma/AUNieHZg74w67GkHFBNB15muOt3sj486QVZk= +github.com/dmarkham/enumer v1.5.9/go.mod h1:e4VILe2b1nYK3JKJpRmNdl5xbDQvELc6tQ8b+GsGk6E= +github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI= +github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE= +github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o= +github.com/eclipse/paho.mqtt.golang v1.5.0/go.mod h1:du/2qNQVqJf/Sqs4MEL77kR8QTqANF7XU7Fk0aOTAgk= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= +github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.13.0 h1:HzkeUz1Knt+3bK+8LG1bxOO/jzWZmdxpwC51i202les= +github.com/envoyproxy/go-control-plane v0.13.0/go.mod h1:GRaKG3dwvFoTg4nj7aXdZnvMg4d7nvT/wl9WgVXn3Q8= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= +github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fasthttp/websocket v1.5.3 h1:TPpQuLwJYfd4LJPXvHDYPMFWbLjsT91n3GpWtCQtdek= +github.com/fasthttp/websocket v1.5.3/go.mod h1:46gg/UBmTU1kUaTcwQXpUxtRwG2PvIZYeA8oL6vF3Fs= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw= +github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw= +github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg= +github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo= +github.com/go-faster/jx v1.1.0 h1:ZsW3wD+snOdmTDy9eIVgQdjUpXRRV4rqW8NS3t+20bg= +github.com/go-faster/jx v1.1.0/go.mod h1:vKDNikrKoyUmpzaJ0OkIkRQClNHFX/nF3dnTJZb3skg= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.17.2/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.17.2/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= +github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.17.2/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= +github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.17.2/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.18.0/go.mod h1:uI6pHuxWYTy94zZxgcwJkUWa9wbIlhteGfloI10GD4U= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.3/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.17.2/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.4/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= +github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= +github.com/go-openapi/validate v0.17.2/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 h1:JVrqSeQfdhYRFk24TvhTZWU0q8lfCojxZQFi3Ou7+uY= +github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +github.com/go-zookeeper/zk v1.0.2 h1:4mx0EYENAdX/B/rbunjlt5+4RTA/a9SMHBRuSKdGxPM= +github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo= +github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= +github.com/gofiber/websocket/v2 v2.2.1 h1:C9cjxvloojayOp9AovmpQrk8VqvVnT8Oao3+IUygH7w= +github.com/gofiber/websocket/v2 v2.2.1/go.mod h1:Ao/+nyNnX5u/hIFPuHl28a+NIkrqK7PRimyKaj4JxVU= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk= +github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.2.2-0.20190730201129-28a6bbf47e48/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI= +github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc= +github.com/google/flatbuffers v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI= +github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/gnxi v0.0.0-20231026134436-d82d9936af15 h1:EETGSLGKBReUUYZdztSp45EzTE6CHw2qMKIfyPrgp6c= +github.com/google/gnxi v0.0.0-20231026134436-d82d9936af15/go.mod h1:w8XuCWhpJuVsGdFLU9bLN9CBLROXSDp9tO1SFgg2l+4= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190723021845-34ac40c74b70/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= +github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= +github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= +github.com/gopacket/gopacket v1.3.0 h1:MouZCc+ej0vnqzB0WeiaO/6+tGvb+KU7UczxoQ+X0Yc= +github.com/gopacket/gopacket v1.3.0/go.mod h1:WnFrU1Xkf5lWKV38uKNR9+yYtppn+ZYzOyNqMeH4oNE= +github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gophercloud/gophercloud v0.25.0 h1:C3Oae7y0fUVQGSsBrb3zliAjdX+riCSEh4lNMejFNI4= +github.com/gophercloud/gophercloud v0.25.0/go.mod h1:Q8fZtyi5zZxPS/j9aj3sSxtvj41AdQMDwyo1myduD5c= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E= +github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grafana/pyroscope-go v1.2.0 h1:aILLKjTj8CS8f/24OPMGPewQSYlhmdQMBmol1d3KGj8= +github.com/grafana/pyroscope-go v1.2.0/go.mod h1:2GHr28Nr05bg2pElS+dDsc98f3JTUh2f6Fz1hWXrqwk= +github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg= +github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.4/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/api v1.29.2 h1:aYyRn8EdE2mSfG14S1+L9Qkjtz8RzmaWh6AcNGRNwPw= +github.com/hashicorp/consul/api v1.29.2/go.mod h1:0YObcaLNDSbtlgzIRtmRXI1ZkeuK0trCBxwZQ4MYnIk= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/cronexpr v1.1.1 h1:NJZDd87hGXjoZBdvyCF9mX4DCq5Wy7+A/w+A7q0wn6c= +github.com/hashicorp/cronexpr v1.1.1/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= +github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/nomad/api v0.0.0-20220629141207-c2428e1673ec h1:jAF71e0KoaY2LJlRsRxxGz6MNQOG5gTBIc+rklxfNO0= +github.com/hashicorp/nomad/api v0.0.0-20220629141207-c2428e1673ec/go.mod h1:jP79oXjopTyH6E8LF0CEMq67STgrlmBRIyijA0tuR5o= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.8.3/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k= +github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/hetznercloud/hcloud-go v1.35.0 h1:sduXOrWM0/sJXwBty7EQd7+RXEJh5+CsAGQmHshChFg= +github.com/hetznercloud/hcloud-go v1.35.0/go.mod h1:mepQwR6va27S3UQthaEPGS86jtzSY9xWL1e9dyxXpgA= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb v1.7.7/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/influxdata/line-protocol/v2 v2.2.1 h1:EAPkqJ9Km4uAxtMRgUubJyqAr6zgWM0dznKMLRauQRE= +github.com/influxdata/line-protocol/v2 v2.2.1/go.mod h1:DmB3Cnh+3oxmG6LOBIxce4oaL4CPj3OmMPgvauXh+tM= +github.com/influxdata/telegraf v1.33.0 h1:9fSe7G47R5VqUdljpZXyZWEfjw2PiAuELVAqNo5HInI= +github.com/influxdata/telegraf v1.33.0/go.mod h1:/KyX97cyEkkWZwquCL7O763NVe15+z6FK20OFdoAb6A= +github.com/influxdata/toml v0.0.0-20190415235208-270119a8ce65 h1:vvyMtD5LTJc1W9sQKjDkAWdcg0478CszSdzlHtiAXCY= +github.com/influxdata/toml v0.0.0-20190415235208-270119a8ce65/go.mod h1:zApaNFpP/bTpQItGZNNUMISDMDAnTXu9UqJ4yT3ocz8= +github.com/ionos-cloud/sdk-go/v6 v6.1.0 h1:0EZz5H+t6W23zHt6dgHYkKavr72/30O9nA97E3FZaS4= +github.com/ionos-cloud/sdk-go/v6 v6.1.0/go.mod h1:Ox3W0iiEz0GHnfY9e5LmAxwklsxguuNFEUSu0gVRTME= +github.com/jeremywohl/flatten/v2 v2.0.0-20211013061545-07e4a09fb8e4 h1:eA9wi6ZzpIRobvXkn/S2Lyw1hr2pc71zxzOPl7Xjs4w= +github.com/jeremywohl/flatten/v2 v2.0.0-20211013061545-07e4a09fb8e4/go.mod h1:s9g9Dfls+aEgucKXKW+i8MRZuLXT2MrD/WjYpMnWfOw= +github.com/jessevdk/go-flags v0.0.0-20180331124232-1c38ed7ad0cc/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jhump/protoreflect v1.16.0 h1:54fZg+49widqXYQ0b+usAFHbMkBGR4PpXrsHc8+TBDg= +github.com/jhump/protoreflect v1.16.0/go.mod h1:oYPd7nPvcBw/5wlDfm/AVmU9zH9BgqGCI469pGxfj/8= +github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= +github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= +github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= +github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0= +github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= +github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= +github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/logfmt v0.0.0-20210122060352-19f9bcb100e6 h1:ZK1mH67KVyVW/zOLu0xLva+f6xJ8vt+LGrkQq5FJYLY= +github.com/kr/logfmt v0.0.0-20210122060352-19f9bcb100e6/go.mod h1:JIiJcj9TX57tEvCXjm6eaHd2ce4pZZf9wzYuThq45u8= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v0.0.0-20160406211939-eadb3ce320cb/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= +github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= +github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= +github.com/lestrrat-go/strftime v1.1.0 h1:gMESpZy44/4pXLO/m+sL0yBd1W6LjgjrrD4a68Gapyg= +github.com/lestrrat-go/strftime v1.1.0/go.mod h1:uzeIB52CeUJenCo1syghlugshMysrqUT51HlxphXVeI= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/linkedin/goavro/v2 v2.13.0 h1:L8eI8GcuciwUkt41Ej62joSZS4kKaYIUdze+6for9NU= +github.com/linkedin/goavro/v2 v2.13.0/go.mod h1:KXx+erlq+RPlGSPmLF7xGo6SAbh8sCQ53x064+ioxhk= +github.com/linode/linodego v1.8.0 h1:7B2UaWu6C48tZZZrtINWRElAcwzk4TLnL9USjKf3xm0= +github.com/linode/linodego v1.8.0/go.mod h1:heqhl91D8QTPVm2k9qZHP78zzbOdTFLXE9NJc3bcc50= +github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a h1:3Bm7EwfUQUvhNeKIkUct/gl9eod1TcXuj8stxvi/GoI= +github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/m3db/prometheus_remote_client_golang v0.4.4 h1:DsAIjVKoCp7Ym35tAOFL1OuMLIdIikAEHeNPHY+yyM8= +github.com/m3db/prometheus_remote_client_golang v0.4.4/go.mod h1:wHfVbA3eAK6dQvKjCkHhusWYegCk3bDGkA15zymSHdc= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mcuadros/go-defaults v1.2.0 h1:FODb8WSf0uGaY8elWJAkoLL0Ri6AlZ1bFlenk56oZtc= +github.com/mcuadros/go-defaults v1.2.0/go.mod h1:WEZtHEVIGYVDqkKSWBdWKUVdRyKlMfulPaGDWIVeCWY= +github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= +github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= +github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ= +github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= +github.com/metrico/cloki-config v0.0.82 h1:eSRD1qQiKyBR5bQT1hMWKGFnp2TX6bkfkUPU14TzTqI= +github.com/metrico/cloki-config v0.0.82/go.mod h1:zjxnDFtbyI08jMKjy12sleeQZjbJM2Vpngf+8gwm/4I= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= +github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 h1:BpfhmLKZf+SjVanKKhCgf3bg+511DmU9eDQTen7LLbY= +github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/mochi-co/mqtt v1.3.2 h1:cRqBjKdL1yCEWkz/eHWtaN/ZSpkMpK66+biZnrLrHC8= +github.com/mochi-co/mqtt v1.3.2/go.mod h1:o0lhQFWL8QtR1+8a9JZmbY8FhZ89MF8vGOGHJNFbCB8= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= +github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nats.go v1.36.0 h1:suEUPuWzTSse/XhESwqLxXGuj8vGRuPRoG7MoRN/qyU= +github.com/nats-io/nats.go v1.36.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= +github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/netsampler/goflow2/v2 v2.2.1 h1:QzrtWS/meXsqCLv68hdouL+09NfuLKrCoVDJ1xfmuoE= +github.com/netsampler/goflow2/v2 v2.2.1/go.mod h1:057wOc/Xp7c+hUwRDB7wRqrx55m0r3vc7J0k4NrlFbM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/ulid v0.0.0-20170117200651-66bb6560562f/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/openconfig/gnmi v0.11.0 h1:H7pLIb/o3xObu3+x0Fv9DCK7TH3FUh7mNwbYe+34hFw= +github.com/openconfig/gnmi v0.11.0/go.mod h1:9oJSQPPCpNvfMRj8e4ZoLVAw4wL8HyxXbiDlyuexCGU= +github.com/openconfig/goyang v1.6.0 h1:JjnPbLY1/y28VyTO67LsEV0TaLWNiZyDcsppGq4F4is= +github.com/openconfig/goyang v1.6.0/go.mod h1:sdNZi/wdTZyLNBNfgLzmmbi7kISm7FskMDKKzMY+x1M= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= +github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/name v1.0.1 h1:9lnXOHeqeHHnWLbKfH6X98+4+ETVqFqxN09UXSjcMb0= +github.com/pascaldekloe/name v1.0.1/go.mod h1:Z//MfYJnH4jVpQ9wkclwu2I2MkHmXTlT9wR5UZScttM= +github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= +github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= +github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/peterbourgon/unixtransport v0.0.4 h1:UTF0FxXCAglvoZz9jaGPYjEg52DjBLDYGMJvJni6Tfw= +github.com/peterbourgon/unixtransport v0.0.4/go.mod h1:o8aUkOCa8W/BIXpi15uKvbSabjtBh0JhSOJGSfoOhAU= +github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 h1:jYi87L8j62qkXzaYHAQAhEapgukhenIMZRBKTNRLHJ4= +github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/prometheus/alertmanager v0.18.0/go.mod h1:WcxHBl40VSPuOaqWae6l6HpnEOVRIycEJ7i9iYkadEE= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= +github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= +github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/prometheus v0.0.0-20180315085919-58e2a31db8de/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s= +github.com/prometheus/prometheus v0.0.0-20190818123050-43acd0e2e93f/go.mod h1:rMTlmxGCvukf2KMu3fClMDKLLoJ5hl61MhcJ7xKakf0= +github.com/prometheus/prometheus v1.8.2-0.20220714142409-b41e0750abf5 h1:y/PIlkj30dGwBdI2HHbYx3Z6eFRmbpznt/Bwp/Ca6qg= +github.com/prometheus/prometheus v1.8.2-0.20220714142409-b41e0750abf5/go.mod h1:egARUgz+K93zwqsVIAneFlLZefyGOON44WyAp4Xqbbk= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samuel/go-zookeeper v0.0.0-20190810000440-0ceca61e4d75/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/satori/go.uuid v0.0.0-20160603004225-b111a074d5ef/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9 h1:0roa6gXKgyta64uqh52AQG3wzZXH21unn+ltzQSXML0= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= +github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= +github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= +github.com/shirou/gopsutil/v4 v4.24.10 h1:7VOzPtfw/5YDU+jLEoBwXwxJbQetULywoSV4RYY7HkM= +github.com/shirou/gopsutil/v4 v4.24.10/go.mod h1:s4D/wg+ag4rG0WO7AiTj2BeYCRhym0vM7DHbZRxnIT8= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/vfsgen v0.0.0-20180825020608-02ddb050ef6b/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY= +github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= +github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/srebhan/cborquery v1.0.1 h1:cFG1falVzmlfyVI8tY6hYM7RQqLxFzt9STusdxHoy0U= +github.com/srebhan/cborquery v1.0.1/go.mod h1:GgsaIoCW+qlqyU+cjSeOpaWhbiiMVkA0uU/H3+PWvjQ= +github.com/srebhan/protobufquery v1.0.1 h1:V5NwX0GAQPPghWpoD9Pkm85j66CwISZ/zZW4grzayWs= +github.com/srebhan/protobufquery v1.0.1/go.mod h1:qMMAteTXwcXz+iV2pn7Kpvhzu3JSK2LIABCRL6BHcCY= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= +github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo= +github.com/testcontainers/testcontainers-go v0.34.0/go.mod h1:6P/kMkQe8yqPHfPWNulFGdFHTD8HB2vLq/231xY2iPQ= +github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/tinylru v1.1.0/go.mod h1:3+bX+TJ2baOLMWTnlyNWHh4QMnFyARg2TLTQ6OFbzw8= +github.com/tidwall/tinylru v1.2.1 h1:VgBr72c2IEr+V+pCdkPZUwiQ0KJknnWIYbhxAVkYfQk= +github.com/tidwall/tinylru v1.2.1/go.mod h1:9bQnEduwB6inr2Y7AkBP7JPgCkyrhTV/ZpX0oOOpBI4= +github.com/tidwall/wal v1.1.7 h1:emc1TRjIVsdKKSnpwGBAcsAGg0767SvUk8+ygx7Bb+4= +github.com/tidwall/wal v1.1.7/go.mod h1:r6lR1j27W9EPalgHiB7zLJDYu3mzW5BQP5KrzBpYY/E= +github.com/tinylib/msgp v1.2.0 h1:0uKB/662twsVBpYUPbokj4sTSKhWFKB7LopO2kWK8lY= +github.com/tinylib/msgp v1.2.0/go.mod h1:2vIGs3lcUo8izAATNobrCHevYZC/LMsJtw4JPiYPHro= +github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= +github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= +github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= +github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0= +github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ= +github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= +github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk= +github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs= +github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= +github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= +github.com/vjeantet/grok v1.0.1 h1:2rhIR7J4gThTgcZ1m2JY4TrJZNgjn985U28kT2wQrJ4= +github.com/vjeantet/grok v1.0.1/go.mod h1:ax1aAchzC6/QMXMcyzHQGZWaW1l195+uMYIkCWPCNIo= +github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= +github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.0.4/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/collector/pdata v0.62.1 h1:c0GOy41tUmkbZnJQCsKuVrXE9W9AZE1uJC8oAZ0OBys= +go.opentelemetry.io/collector/pdata v0.62.1/go.mod h1:ziGuxiR4TVSZ7pT+j1t58zYFVQtWwiWi9ng9EFmp5U0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo= +go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= +go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= +go.step.sm/crypto v0.54.0 h1:V8p+12Ld0NRA/RBMYoKXA0dWmVKZSdCwP56IwzweT9g= +go.step.sm/crypto v0.54.0/go.mod h1:vQJyTngfZDW+UyZdFzOMCY/txWDAmcwViEUC7Gn4YfU= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= +golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180805044716-cb6730876b98/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190118193359-16909d206f00/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190813034749-528a2984e271/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 h1:pgr/4QbFyktUv9CtQ/Fq4gzEE6/Xs7iCXbktaGzLHbQ= +google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697/go.mod h1:+D9ySVjN8nY8YCVjc5O7PZDIdZporIDY3KaGfJunh88= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 h1:LWZqQOEjDyONlF1H6afSWpAL/znlREo2tHfLoe+8LMA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= +google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M= +gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= +gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.0.0-20190813020757-36bff7324fb7/go.mod h1:3Iy+myeAORNCLgjd/Xu9ebwN7Vh59Bw0vh9jhoX+V58= +k8s.io/api v0.22.1/go.mod h1:bh13rkTp3F1XEaLGykbyRD2QaTTzPm0e/BMd8ptFONY= +k8s.io/api v0.24.17 h1:ILPpMleNDZbMJwopUBOVWtmCq3xBAj/4gJEUicy6QGs= +k8s.io/api v0.24.17/go.mod h1:Ff5rnpz9qMj3/tXXA504wdk7Mf9zW3JSNWp5tf80VMQ= +k8s.io/apimachinery v0.0.0-20190809020650-423f5d784010/go.mod h1:Waf/xTS2FGRrgXCkO5FP3XxTOWh0qLf2QhL1qFZZ/R8= +k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= +k8s.io/apimachinery v0.24.17 h1:mewWCeZ3Swr4EAfatVAhHXJHGzCHojphWA/5UJW4pPY= +k8s.io/apimachinery v0.24.17/go.mod h1:kSzhCwldu9XB172NDdLffRN0sJ3x95RR7Bmyc4SHhs0= +k8s.io/client-go v0.22.1 h1:jW0ZSHi8wW260FvcXHkIa0NLxFBQszTlhiAVsU5mopw= +k8s.io/client-go v0.22.1/go.mod h1:BquC5A4UOo4qVDUtoc04/+Nxp1MeHcVc1HJm1KmG8kk= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4= +k8s.io/kube-openapi v0.0.0-20190722073852-5e22f3d471e6/go.mod h1:RZvgC8MSN6DjiMV6oIfEE9pDL9CYXokkfaCKZeHm3nc= +k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20190809000727-6c36bc71fc4a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 h1:MDF6h2H/h4tbzmtIKTuctcwZmY0tY9mD9fNT47QO6HI= +k8s.io/utils v0.0.0-20240921022957-49e7df575cb6/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index ba622f5f..00000000 --- a/jest.config.js +++ /dev/null @@ -1,7 +0,0 @@ -const path = require('path') -module.exports = { - setupFilesAfterEnv: [path.join(__dirname, '/test/jest.setup.js')], - moduleNameMapper: { - '^axios$': require.resolve('axios'),//'axios/dist/node/axios.cjs' - } -} diff --git a/lib/bun_wrapper.js b/lib/bun_wrapper.js deleted file mode 100644 index 2ce2eeeb..00000000 --- a/lib/bun_wrapper.js +++ /dev/null @@ -1,181 +0,0 @@ -const { Transform } = require('stream') -const log = require('./logger') -const { EventEmitter } = require('events') -const zlib = require('zlib') - -class BodyStream extends Transform { - _transform (chunk, encoding, callback) { - callback(null, chunk) - } - - once (event, listerer) { - const self = this - const _listener = (e) => { - listerer(e) - self.removeListener(event, _listener) - } - this.on(event, _listener) - } -} - -const wrapper = (handler, parsers) => { - /** - * @param ctx {Request} - */ - const res = async (ctx, server) => { - let response = '' - let status = 200 - let reqBody = '' - let headers = {} - log.info(`${ctx.url}`) - - const stream = new BodyStream() - setTimeout(async () => { - if (!ctx.body) { - stream.end() - return - } - for await (const chunk of ctx.body) { - stream.write(chunk) - } - stream.end() - }) - const req = { - headers: Object.fromEntries(ctx.headers.entries()), - raw: stream, - log: log, - params: ctx.params || {}, - query: {}, - method: ctx.method - } - for (const [key, value] of (new URL(ctx.url)).searchParams) { - if (!(key in req.query)) { - req.query[key] = value - continue - } - req.query[key] = Array.isArray(req.query[key]) - ? [...req.query[key], value] - : [req.query[key], value] - } - const res = { - send: (msg) => { - response = msg - }, - code: (code) => { - status = code - return res - }, - header: (key, value) => { - headers[key] = value - return res - }, - headers: (hdrs) => { - headers = { ...headers, ...hdrs } - return res - } - } - - if (parsers) { - const contentType = (ctx.headers.get('Content-Type') || '') - let ok = false - for (const [type, parser] of Object.entries(parsers)) { - if (type !== '*' && contentType.indexOf(type) > -1) { - log.debug(`parsing ${type}`) - reqBody = await parser(req, stream) - ok = true - log.debug(`parsing ${type} ok`) - } - } - if (!ok && parsers['*']) { - log.debug('parsing *') - reqBody = await parsers['*'](req, stream) - ok = true - log.debug('parsing * ok') - } - if (!ok) { - throw new Error('undefined content type ' + contentType) - } - } - - req.body = reqBody || stream - - let result = handler(req, res) - if (result && result.then) { - result = await result - } - if (result && result.on) { - response = '' - result.on('data', (d) => { - response += d - }) - await new Promise((resolve, reject) => { - result.on('end', resolve) - result.on('error', reject) - result.on('close', resolve) - }) - result = null - } - if (result) { - response = result - } - if (response instanceof Object && typeof response !== 'string' && !Buffer.isBuffer(response)) { - headers['Content-Type'] = 'application/json' - response = JSON.stringify(response) - } - if (response && (ctx.headers.get('accept-encoding') || '').indexOf('gzip') !== -1) { - if (response.on) { - const _r = zlib.createGzip() - response.pipe(_r) - response = _r - } else { - response = Bun.gzipSync(response) - } - headers['Content-Encoding'] = 'gzip' - } - return new Response(response, { status: status, headers: headers }) - } - return res -} - -const wsWrapper = (handler) => { - /** - * @param ctx {Request} - */ - const res = { - open: async (ctx, server) => { - const req = { - headers: Object.fromEntries(ctx.data.ctx.headers.entries()), - log: log, - query: {} - } - for (const [key, value] of (new URL(ctx.data.ctx.url)).searchParams) { - if (!(key in req.query)) { - req.query[key] = value - continue - } - req.query[key] = Array.isArray(req.query[key]) - ? [...req.query[key], value] - : [req.query[key], value] - } - - ctx.closeEmitter = new EventEmitter() - ctx.closeEmitter.send = ctx.send.bind(ctx) - - const ws = { - socket: ctx.closeEmitter - } - - const result = handler(ws, { query: req.query }) - if (result && result.then) { - await result - } - }, - close: (ctx) => { ctx.closeEmitter.emit('close') } - } - return res -} - -module.exports = { - wrapper, - wsWrapper -} diff --git a/lib/cliql.js b/lib/cliql.js deleted file mode 100644 index 1c4a1f39..00000000 --- a/lib/cliql.js +++ /dev/null @@ -1,53 +0,0 @@ -const { toJSON } = require('./utils') -const RATEQUERY = /(.*) by \((.*)\) \(rate\((.*)\[(.*)\]\)\) from (.*)\.(.*)/ -const RATEQUERYWHERE = /(.*) by \((.*)\) \(rate\((.*)\[(.*)\]\)\) from (.*)\.(.*) (?:where|WHERE?) (.*)/ -const RATEQUERYNOWHERE = /(.*) by \((.*)\) \(rate\((.*)\[(.*)\]\)\) from (.*)\.([\S]+)\s?$/ - -/** - * - * @param query {string} - * @returns {{ - * metric: string, - * interval: (string|number), - * tag: string, - * db: string, - * table: string - * } | undefined} - */ -module.exports.parseCliQL = (query) => { - if (RATEQUERYWHERE.test(query)) { - const s = RATEQUERYWHERE.exec(query) - return { - db: s[5], - table: s[6], - interval: s[4] || 60, - tag: s[2], - metric: s[1] + '(' + s[3] + ')', - where: s[7] - } - } else if (RATEQUERYNOWHERE.test(query)) { - const s = RATEQUERYNOWHERE.exec(query) - return { - db: s[5], - table: s[6], - interval: s[4] || 60, - tag: s[2], - metric: s[1] + '(' + s[3] + ')' - } - } else if (RATEQUERY.test(query)) { - const s = RATEQUERY.exec(query) - return { - db: s[5], - table: s[6], - interval: s[4] || 60, - tag: s[2], - metric: s[1] + '(' + s[3] + ')' - } - } else if (query.startsWith('clickhouse(')) { - let queries = null - const _query = /\{(.*?)\}/g.exec(query)[1] || query - queries = _query //_query.replace(/\!?="/g, ':"') - return toJSON(queries) - } - return undefined -} diff --git a/lib/db/alerting/alertWatcher/MVAlertWatcher.js b/lib/db/alerting/alertWatcher/MVAlertWatcher.js deleted file mode 100644 index 9c6f03af..00000000 --- a/lib/db/alerting/alertWatcher/MVAlertWatcher.js +++ /dev/null @@ -1,51 +0,0 @@ -const { - createAlertViews, - incAlertMark, - getAlerts, - dropOutdatedParts -} = require('../../clickhouse_alerting') -const transpiler = require('../../../../parser/transpiler') -const { samplesTableName } = require('../../../utils') -const AlertWatcher = require('./alertWatcher') - -class MVAlertWatcher extends AlertWatcher { - /** - * - * @param nsName {string} - * @param group {alerting.group | alerting.objGroup} - * @param rule {alerting.rule} - */ - constructor (nsName, group, rule) { - super(nsName, group, rule) - this.mv = true - } - - async _createViews () { - /** @type {{query: Select, stream: (function(DataStream): DataStream)[]}} */ - const query = transpiler.transpileTail({ - query: this.rule.expr, - samplesTable: samplesTableName, - rawRequest: true, - suppressTime: true - }) - if (query.stream && query.stream.length) { - throw new Error(`Query ${this.rule.expr} is not supported`) - } - query.query.order_expressions = [] - return createAlertViews(this.nsName, this.group.name, this.rule.alert, query.query) - } - - /** - * @return {Promise<number>} - * @private - */ - async _checkViews () { - const [mark, newMark] = await incAlertMark(this.nsName, this.group.name, this.rule.alert) - const lastAlert = await getAlerts(this.nsName, this.group.name, this.rule.alert, mark) - await this.sendTextAlerts(lastAlert) - await dropOutdatedParts(this.nsName, this.group.name, this.rule.alert, mark) - return newMark - } -} - -module.exports = MVAlertWatcher diff --git a/lib/db/alerting/alertWatcher/alertWatcher.js b/lib/db/alerting/alertWatcher/alertWatcher.js deleted file mode 100644 index b6c825b5..00000000 --- a/lib/db/alerting/alertWatcher/alertWatcher.js +++ /dev/null @@ -1,144 +0,0 @@ -const { - getLastCheck, - dropAlertViews -} = require('../../clickhouse_alerting') -const { durationToMs, parseLabels, errors } = require('../../../../common') -const { alert } = require('../alertmanager') -const compiler = require('../../../../parser/bnf') -const logger = require('../../../logger') - -class AlertWatcher { - /** - * - * @param nsName {string} - * @param group {alerting.group | alerting.objGroup} - * @param rule {alerting.rule} - */ - constructor (nsName, group, rule) { - this.nsName = nsName - this.group = group - this.rule = rule - } - - async init () { - await this._createViews() - } - - /** - * @param group {alerting.group | alerting.objGroup} - * @param rule {alerting.rule} - * @returns {Promise<void>} - */ - async edit (group, rule) { - this.rule = rule - this.group = group - } - - async drop () { - this.stop() - await this._dropViews() - } - - stop () { - if (this.interval) { - clearInterval(this.interval) - this.interval = undefined - } - } - - run () { - if (this.interval) { - return - } - const self = this - this.interval = setInterval(() => { - self.check().catch(e => logger.error(e)) - }, 10000) - } - - async _loadLastCheck () { - this.lastCheck = await getLastCheck(this.nsName, this.group.name, this.rule.alert) - } - - async check () { - try { - if (typeof this.lastCheck === 'undefined') { - await this._loadLastCheck() - } - if (Date.now() - this.lastCheck < durationToMs(this.group.interval)) { - return - } - this.lastCheck = await this._checkViews() - this.health = 'ok' - this.lastError = '' - } catch (err) { - logger.error(err) - this.health = 'error' - this.lastError = err.message - } - } - - _dropViews () { - return dropAlertViews(this.nsName, this.group.name, this.rule.alert) - } - - assertExpr () { - compiler.ParseScript(this.rule.expr.trim()) - for (const lbl of Object.keys(this.rule.labels || {})) { - if (!lbl.match(/[a-zA-Z_][a-zA-Z_0-9]*/)) { - throw new errors.QrynError(400, 'Bad request', `Label ${lbl} is invalid`) - } - } - for (const ann of Object.keys(this.rule.annotations || {})) { - if (!ann.match(/[a-zA-Z_][a-zA-Z_0-9]*/)) { - throw new errors.QrynError(400, 'Bad request', `Annotation ${ann} is invalid`) - } - } - } - - /** - * - * @param alerts {{ - * labels: Object<string, string>, - * extra_labels: Object<string, string>, - * string: string - * }[]} - * @returns {Promise<void>} - */ - async sendTextAlerts (alerts) { - if (!alerts || !alerts.length) { - this.state = 'normal' - this.lastAlert = null - return - } - const self = this - this.state = 'firing' - const _alerts = alerts.map(e => { - const labels = e.extra_labels - ? { ...parseLabels(e.labels), ...parseLabels(e.extra_labels) } - : parseLabels(e.labels) - return { - labels: { - ...(self.rule.labels || {}), - ...(labels) - }, - annotations: self.rule.annotations || {}, - message: e.string.toString() - } - }) - this.lastAlert = _alerts[_alerts.length - 1] - this.firingSince = Date.now() - await alert(self.rule.alert, _alerts) - } - - getLastAlert () { - return this.state === 'firing' - ? { - ...this.lastAlert, - activeAt: this.firingSince, - state: 'firing' - } - : undefined - } -} -module.exports = AlertWatcher diff --git a/lib/db/alerting/alertWatcher/callbackCliqlAlertWatcher.js b/lib/db/alerting/alertWatcher/callbackCliqlAlertWatcher.js deleted file mode 100644 index aafd6535..00000000 --- a/lib/db/alerting/alertWatcher/callbackCliqlAlertWatcher.js +++ /dev/null @@ -1,61 +0,0 @@ -const { - incAlertMark -} = require('../../clickhouse_alerting') -const { scanClickhouse } = require('../../clickhouse') -const CallbackTimeSeriesAlertWatcher = require('./callbackTimeSeriesAlertWatcher') -const { parseCliQL } = require('../../../cliql') - -class CallbackCliqlAlertWatcher extends CallbackTimeSeriesAlertWatcher { - assertExpr () { } - /** - * @return {Promise<number>} - * @private - */ - async _checkViews () { - this.lastCheck = this.lastCheck || Date.now() - const newMark = Date.now() - const params = parseCliQL(this.rule.expr) - const from = newMark - parseInt(params.interval) * 1000 - let active = false - const alerts = [] - await new Promise((resolve, reject) => { - try { - scanClickhouse(params, { - code: () => {}, - send: (data) => { - if (data.data && data.data.result) { - for (const metric of data.data.result) { - for (const val of metric.values) { - active = true - alerts.push({ - labels: metric.metric, - string: val[1] - }) - } - } - resolve(alerts) - return - } - reject(new Error(data)) - } - }, { - start: from + '000000', - end: newMark + '000000', - shift: from - }) - } catch (e) { - reject(e) - } - }) - await this.sendTextAlerts(alerts) - await incAlertMark(this.nsName, this.group.name, this.rule.alert, - (newMark * 2) + (active ? 1 : 0) - ) - await incAlertMark(this.nsName, this.group.name, this.rule.alert, - this.activeSince, 1 - ) - return newMark - } -} - -module.exports = CallbackCliqlAlertWatcher diff --git a/lib/db/alerting/alertWatcher/callbackLogAlertWatcher.js b/lib/db/alerting/alertWatcher/callbackLogAlertWatcher.js deleted file mode 100644 index c12951bc..00000000 --- a/lib/db/alerting/alertWatcher/callbackLogAlertWatcher.js +++ /dev/null @@ -1,56 +0,0 @@ -const { - dropAlertViews, - incAlertMark, - createMarksTable -} = require('../../clickhouse_alerting') -const transpiler = require('../../../../parser/transpiler') -const { getClickhouseStream, preprocessStream } = require('../../clickhouse') -const AlertWatcher = require('./alertWatcher') - -class CallbackLogAlertWatcher extends AlertWatcher { - _dropViews () { - return dropAlertViews(this.nsName, this.group.name, this.rule.alert) - } - - async _createViews () { - return createMarksTable(this.nsName, this.group.name, this.rule.alert) - } - - /** - * @return {Promise<number>} - * @private - */ - async _checkViews () { - this.lastCheck = this.lastCheck || Date.now() - const lastMark = this.lastCheck - let newMark = 0 - const query = transpiler.transpile({ - query: this.rule.expr, - rawRequest: true, - start: `${lastMark}000000`, - end: newMark + '000000', - limit: 1000 - }) - const _stream = await getClickhouseStream(query) - const stream = preprocessStream(_stream, query.stream) - let alerts = [] - for await (const e of stream.toGenerator()()) { - if (!e || !e.labels) { - continue - } - newMark = Math.max(newMark, Math.floor(parseInt(e.timestamp_ns) / 1000000)) - alerts.push(e) - if (alerts.length > 100) { - await this.sendTextAlerts(alerts) - alerts = [] - } - } - await this.sendTextAlerts(alerts) - alerts = [] - newMark = newMark || lastMark - const marks = await incAlertMark(this.nsName, this.group.name, this.rule.alert, newMark) - return marks[1] - } -} - -module.exports = CallbackLogAlertWatcher diff --git a/lib/db/alerting/alertWatcher/callbackTimeSeriesAlertWatcher.js b/lib/db/alerting/alertWatcher/callbackTimeSeriesAlertWatcher.js deleted file mode 100644 index 6ead8710..00000000 --- a/lib/db/alerting/alertWatcher/callbackTimeSeriesAlertWatcher.js +++ /dev/null @@ -1,79 +0,0 @@ -const { - dropAlertViews, - incAlertMark, - createMarksTable, getLastCheck -} = require('../../clickhouse_alerting') -const transpiler = require('../../../../parser/transpiler') -const { getClickhouseStream, preprocessStream } = require('../../clickhouse') -const AlertWatcher = require('./alertWatcher') -const { sharedParamNames } = require('../../../../parser/registry/common') -const { durationToMs } = require('../../../../common') - -class CallbackTimeSeriesAlertWatcher extends AlertWatcher { - _dropViews () { - return dropAlertViews(this.nsName, this.group.name, this.rule.alert) - } - - async _createViews () { - return createMarksTable(this.nsName, this.group.name, this.rule.alert) - } - - async _loadLastCheck () { - const last = parseInt(await getLastCheck(this.nsName, this.group.name, this.rule.alert)) - this.active = last % 2 - this.lastCheck = Math.floor(last / 2) - this.activeSince = parseInt(await getLastCheck(this.nsName, this.group.name, this.rule.alert, 1)) - } - - /** - * @return {Promise<number>} - * @private - */ - async _checkViews () { - this.lastCheck = this.lastCheck || Date.now() - const lastMark = this.lastCheck - const newMark = Date.now() - const query = transpiler.transpile({ - query: this.rule.expr, - rawRequest: true, - start: `${lastMark}000000`, - end: Date.now() + '000000', - limit: 1000, - rawQuery: true - }) - const from = newMark - query.query.ctx.duration - query.query.getParam('timestamp_shift').set(from) - query.query.getParam(sharedParamNames.from).set(from + '000000') - query.query.getParam(sharedParamNames.to).set(newMark + '000000') - const _stream = await getClickhouseStream({ query: query.query.toString() }) - const stream = preprocessStream(_stream, query.stream) - let active = false - const activeRows = [] - for await (const e of stream.toGenerator()()) { - if (!e || !e.labels) { - continue - } - active = true - activeRows.push({ ...e, string: e.value }) - } - if (active && !this.active) { - this.activeSince = newMark - } - this.active = active - const durationMS = durationToMs(this.rule.for) - if (this.active && newMark - durationMS >= this.activeSince) { - await this.sendTextAlerts(activeRows) - } else { - await this.sendTextAlerts(undefined) - } - await incAlertMark(this.nsName, this.group.name, this.rule.alert, - (newMark * 2) + (active ? 1 : 0) - ) - await incAlertMark(this.nsName, this.group.name, this.rule.alert, - this.activeSince, 1 - ) - return newMark - } -} - -module.exports = CallbackTimeSeriesAlertWatcher diff --git a/lib/db/alerting/alertWatcher/index.js b/lib/db/alerting/alertWatcher/index.js deleted file mode 100644 index 7f30c9f9..00000000 --- a/lib/db/alerting/alertWatcher/index.js +++ /dev/null @@ -1,32 +0,0 @@ -const { transpile } = require('../../../../parser/transpiler') -const CallbackLogAlertWatcher = require('./callbackLogAlertWatcher') -const CallbackTimeSeriesAlertWatcher = require('./callbackTimeSeriesAlertWatcher') -const CallbackCliqlAlertWatcher = require('./callbackCliqlAlertWatcher') -const MVAlertWatcher = require('./MVAlertWatcher') -const { parseCliQL } = require('../../../cliql') -const {clusterName} = require('../../../../common') -/** - * @param nsName {string} - * @param group {alerting.group | alerting.objGroup} - * @param rule {alerting.rule} - * @returns {AlertWatcher} - */ -module.exports = (nsName, group, rule) => { - const cliQ = parseCliQL(rule.expr) - if (cliQ) { - return new CallbackCliqlAlertWatcher(nsName, group, rule) - } - const q = transpile({ - query: rule.expr, - limit: 1000, - start: 0, - step: 1 - }) - if (q.matrix) { - return new CallbackTimeSeriesAlertWatcher(nsName, group, rule) - } - if ((q.stream && q.stream.length) || clusterName) { - return new CallbackLogAlertWatcher(nsName, group, rule) - } - return new MVAlertWatcher(nsName, group, rule) -} diff --git a/lib/db/alerting/alerting.d.ts b/lib/db/alerting/alerting.d.ts deleted file mode 100644 index 1703ca32..00000000 --- a/lib/db/alerting/alerting.d.ts +++ /dev/null @@ -1,35 +0,0 @@ -export namespace alerting { - interface group { - name: string, - interval: string, - rules?: rule[] - } - interface objGroup { - name: string, - interval: string, - rules: {[key: string]: rule} - } - interface groupName { - type?: string - ns: string, - group: string - } - interface ruleName { - type?: string, - ns: string, - group: string, - rule: string - } - interface rule { - alert: string, - expr: string, - for: string, - ver: string, - annotations: { - [key: string]: string - } - labels: { - [key: string]: string - } - } -} \ No newline at end of file diff --git a/lib/db/alerting/alertmanager.js b/lib/db/alerting/alertmanager.js deleted file mode 100644 index 2d377ef4..00000000 --- a/lib/db/alerting/alertmanager.js +++ /dev/null @@ -1,34 +0,0 @@ -const axios = require('axios') -const format = require('date-fns/formatRFC3339') -/** - * @param name {string} - * @param alerts {{ - * labels: Object<string, string>, - * annotations: Object<string, string>, - * message: string, - * start? : number | undefined, - * end?: number | undefined - * }[]} - */ -const alert = async (name, alerts) => { - if (!process.env.ALERTMAN_URL) { - return - } - try { - await axios.post(process.env.ALERTMAN_URL + '/api/v2/alerts', alerts.map(e => ({ - labels: { - alertname: name, - ...e.labels - }, - annotations: { ...e.annotations, message: e.message }, - startsAt: e.start ? format(e.start) : undefined, - endsAt: e.end ? format(e.end) : undefined - }))) - } catch (e) { - throw new Error(e.message + (e.response.data ? '\n' + JSON.stringify(e.response.data) : '')) - } -} - -module.exports = { - alert -} diff --git a/lib/db/alerting/index.js b/lib/db/alerting/index.js deleted file mode 100644 index 3eadcf2e..00000000 --- a/lib/db/alerting/index.js +++ /dev/null @@ -1,221 +0,0 @@ -const { - getAlertRules, - deleteAlertRule, - deleteGroup, - getAlertGroups, - putAlertRule -} = require('../clickhouse_alerting') -const factory = require('./alertWatcher') -const logger = require('../../logger') -const utils = require('../../utils') -let enabled = false -/** - * - * @param namespace {string} - * @param group {alerting.group} - * @returns {Promise<void>} - */ -module.exports.setGroup = async (namespace, group) => { - for (const r of group.rules || []) { - r.labels = r.labels || {} - r.annotations = r.annotations || {} - } - /** @type {alerting.rule[]} */ - const rules = group.rules || [] - const rulesToAdd = alerts[namespace] && alerts[namespace][group.name] - ? rules.filter(r => !alerts[namespace][group.name].rules[r.alert]) - : rules - const rulesToDelete = alerts[namespace] && alerts[namespace][group.name] - ? Object.keys(alerts[namespace][group.name].rules) - .filter(k => !rules.some(r => r.alert === k)) - .map(k => alerts[namespace][group.name].rules[k]) - : [] - const rulesToUpdate = alerts[namespace] && alerts[namespace][group.name] - ? rules - .filter(r => alerts[namespace][group.name].rules[r.alert]) - .map(r => [alerts[namespace][group.name].rules[r.alert], r]) - : [] - for (const rul of rulesToAdd) { - rul.ver = utils.schemaVer - const w = factory(namespace, group, rul) - w.assertExpr() - await w.init() - w.run() - rul._watcher = w - await putAlertRule(namespace, group, rul) - addRule(namespace, group, rul) - } - for (const rul of rulesToDelete) { - const w = rul._watcher - await w.drop() - await deleteAlertRule(namespace, group.name, rul.alert) - delRule(namespace, group.name, rul.alert) - } - for (const [_old, _new] of rulesToUpdate) { - _new.ver = utils.schemaVer - if (_old.expr !== _new.expr) { - const w = _old._watcher - await w.drop() - const _w = factory(namespace, group, _new) - _w.assertExpr() - await _w.init() - _w.run() - _new._watcher = _w - await putAlertRule(namespace, group, _new) - addRule(namespace, group, _new) - continue - } - const w = _old._watcher - w.stop() - await w.edit(group, _new) - w.run() - _new._watcher = w - await putAlertRule(namespace, group, _old) - addRule(namespace, group, _new) - } -} - -/** - * - * @param ns {string} - * @param group {alerting.group} - * @param rule {alerting.rule} - */ -const addRule = (ns, group, rule) => { - alerts[ns] = alerts[ns] || {} - alerts[ns][group.name] = alerts[ns][group.name] || {} - alerts[ns][group.name] = { - interval: group.interval, - name: group.name, - rules: alerts[ns][group.name].rules || {} - } - alerts[ns][group.name].rules[rule.alert] = rule -} - -/** - * @param ns {string} - * @param group {string} - * @param rule {string} - */ -const delRule = (ns, group, rule) => { - if (!alerts[ns] || !alerts[ns][group] || !alerts[ns][group].rules[rule]) { - return - } - delete alerts[ns][group].rules[rule] - if (!Object.keys(alerts[ns][group].rules).length) { - delete alerts[ns][group] - } - if (!Object.keys(alerts[ns]).length) { - delete alerts[ns] - } -} - -/** - * - * @returns {Object<string, Object<string, alerting.objGroup>> } namespace - */ -module.exports.getAll = () => { - return alerts -} - -/** - * - * @param ns {string} - * @returns {Object<string, alerting.objGroup>} namespace - */ -module.exports.getNs = (ns) => { - return alerts[ns] -} - -/** - * - * @param ns {string} - * @param grp {string} - * @returns {alerting.objGroup | undefined} group - */ -module.exports.getGroup = (ns, grp) => { - return alerts[ns] && alerts[ns][grp] ? alerts[ns][grp] : undefined -} - -/** - * - * @param ns {string} - * @param grp {string} - * @returns {Promise<void>} - */ -module.exports.dropGroup = async (ns, grp) => { - if (!alerts[ns] || !alerts[ns][grp]) { - return - } - for (const rul of Object.values(alerts[ns][grp].rules)) { - const w = rul._watcher - w.stop() - await w.drop() - await deleteAlertRule(ns, grp, rul.alert) - } - await deleteGroup(ns, grp) - delete alerts[ns][grp] -} - -/** - * - * @param ns {string} - * @returns {Promise<void>} - */ -module.exports.dropNs = async (ns) => { - if (!alerts[ns]) { - return - } - for (const grp of Object.keys(alerts[ns])) { - await module.exports.dropGroup(ns, grp) - } - delete alerts[ns] -} - -module.exports.stop = () => { - for (const ns of Object.values(alerts)) { - for (const group of Object.values(ns)) { - for (const rule of Object.values(group.rules)) { - rule._watcher && rule._watcher.stop() - } - } - } - alerts = {} -} - -module.exports.startAlerting = async () => { - const rules = await getAlertRules() - const groups = await getAlertGroups() - for (const rule of rules) { - rule.labels = rule.labels || {} - rule.annotations = rule.annotations || {} - const group = groups.find(g => - g.name.ns === rule.name.ns && - g.name.group === rule.name.group - ) - if (!group) { - logger.info({ rule }, 'Not found group for rule') - continue - } - const w = factory(rule.name.ns, group.group, rule.rule) - if (w.mv && rule.rule.ver !== utils.schemaVer) { - await w._checkViews() - await w.drop() - await w.init() - rule.rule.ver = utils.schemaVer - await putAlertRule(rule.name.ns, group.group, rule.rule) - } - w.run() - rule.rule._watcher = w - addRule(rule.name.ns, group.group, rule.rule) - } - enabled = true -} - -module.exports.isEnabled = () => enabled - -/** - * - * @type {Object<string, Object<string, alerting.objGroup>>} - */ -let alerts = {} diff --git a/lib/db/clickhouse.js b/lib/db/clickhouse.js deleted file mode 100644 index d8ab954c..00000000 --- a/lib/db/clickhouse.js +++ /dev/null @@ -1,1487 +0,0 @@ -/* - * Qryn DB Adapter for Clickhouse - * (C) 2018-2022 QXIP BV - */ - -const UTILS = require('../utils') -const toJSON = UTILS.toJSON -const logger = require('../logger') -const { formatISO9075 } = require('date-fns') -const { Blob } = require('buffer') -const Zipkin = require('./zipkin') -const Otlp = require('./otlp') -const logfmt = require('logfmt') -const csql = require('@cloki/clickhouse-sql') -const clusterName = require('../../common').clusterName -const dist = clusterName ? '_dist' : '' - -/* DB Helper */ -const ClickHouse = require('@apla/clickhouse') - -const transpiler = require('../../parser/transpiler') -const rotationLabels = process.env.LABELS_DAYS || 7 -const rotationSamples = process.env.SAMPLES_DAYS || 7 -const axios = require('axios') -const { samplesTableName, samplesReadTableName } = UTILS -const path = require('path') -const { Transform } = require('stream') -const { CORS, bun, readonly, boolEnv } = require('../../common') -const clickhouseOptions = require('./clickhouse_options').databaseOptions -const { getClickhouseUrl } = require('./clickhouse_options') - -// External Storage Policy for Tables (S3, MINIO) -const storagePolicy = process.env.STORAGE_POLICY || false -// Clickhouse Distributed Engine setting to skip unavailable shards -const skipUnavailableShards = process.env.SKIP_UNAVAILABLE_SHARDS || false - -const { StringStream, DataStream } = require('scramjet') - -const { parseLabels, hashLabels, isCustomSamplesOrderingRule, isOmitTablesCreation } = require('../../common') - -const { Worker, isMainThread } = require('worker_threads') - -const jsonSerializer = (k, val) => typeof val === 'bigint' - ? val.toString() - : typeof val === 'number' && isNaN(val) - ? 'NaN' - : val - -const createCsvArrayWriter = require('csv-writer').createArrayCsvStringifier - -const capabilities = {} -let state = 'INITIALIZING' - -const clickhouse = new ClickHouse(clickhouseOptions) -let ch - -const conveyor = { - labels: 0, - lastUpdate: 0, - count: async () => { - if (conveyor.lastUpdate < Date.now() - 30000) { - return conveyor.labels - } - try { - const resp = await rawRequest(`SELECT COUNT(1) as c FROM ${UTILS.DATABASE_NAME()}.time_series FORMAT JSON`) - conveyor.labels = resp.data.data[0].c - return conveyor.labels - } catch (e) { - logger.error(e) - } - } -} - -let throttler = null -const resolvers = {} -const rejectors = {} -let first = false -if (isMainThread && !bun()) { - throttler = new Worker(path.join(__dirname, 'throttler.js')) - throttler.on('message', (msg) => { - switch (msg.status) { - case 'ok': - resolvers[msg.id]() - break - case 'err': - rejectors[msg.id](new Error('Database push error')) - break - } - delete resolvers[msg.id] - delete rejectors[msg.id] - }) -} else if (isMainThread && !first) { - first = true - setTimeout(() => { - const _throttler = require('./throttler') - throttler = { - on: _throttler.on, - postMessage: _throttler.postMessage, - removeAllListeners: _throttler.removeAllListeners, - terminate: _throttler.terminate - } - _throttler.init() - throttler.on('message', (msg) => { - switch (msg.status) { - case 'ok': - resolvers[msg.id]() - break - case 'err': - rejectors[msg.id](new Error('Database push error')) - break - } - delete resolvers[msg.id] - delete rejectors[msg.id] - }) - }) -} -// timeSeriesv2Throttler.start(); - -/* Cache Helper */ -const recordCache = require('record-cache') -const { parseMs, DATABASE_NAME } = require('../utils') -let id = 0 -function getThrottlerId () { - id = (id + 1) % 1e6 - return id -} -// Flushing to Clickhouse -const bulk = { - add: (values) => { - const id = getThrottlerId() - return new Promise((resolve, reject) => { - throttler.postMessage({ - type: 'values', - data: values.map(r => JSON.stringify({ - fingerprint: r[0], - timestamp_ns: r[1], - value: r[2], - string: r[3], - type: r[4] - }, jsonSerializer)).join('\n'), - id: id - }) - resolvers[id] = resolve - rejectors[id] = reject - }) - } -} - -const bulkLabels = { - add: (values) => { - return new Promise((resolve, reject) => { - const id = getThrottlerId() - throttler.postMessage({ - type: 'labels', - data: values.map(r => JSON.stringify({ - date: r[0], - fingerprint: r[1], - labels: r[2], - name: r[3], - type: r[4] - }, jsonSerializer)).join('\n'), - id: id - }) - resolvers[id] = resolve - rejectors[id] = reject - }) - } -} - -// In-Memory LRU for quick lookups -const labels = recordCache({ - maxSize: process.env.BULK_MAXCACHE || 50000, - maxAge: 0, - onStale: false -}) - -const checkDB = async function() { - await checkCapabilities() - await samplesReadTable.check() -} - -/* Initialize */ -const initialize = async function (dbName) { - logger.info('Initializing DB... ' + dbName) - const tmp = { ...clickhouseOptions, queryOptions: { database: '' } } - ch = new ClickHouse(tmp) - if (readonly) { - state = 'READY' - return - } - if (!isOmitTablesCreation()) { - const maintain = require('./maintain/index') - await maintain.upgrade({ name: dbName, storage_policy: storagePolicy, skip_unavailable_shards: skipUnavailableShards }) - await maintain.rotate([{ - db: dbName, - samples_days: rotationSamples, - time_series_days: rotationLabels, - storage_policy: storagePolicy - }]) - } else { - logger.info('Omitting tables creation') - } - - state = 'READY' - - reloadFingerprints() -} - -const checkCapabilities = async () => { - logger.info('Checking clickhouse capabilities') - // qryn doesn't use LIVE VIEW after ClickHouse dropped WITH TIMEOUT clause support - capabilities.liveView = false -} - -const reloadFingerprints = function () { - return; - logger.info('Reloading Fingerprints...') - const selectQuery = `SELECT DISTINCT fingerprint, labels FROM ${clickhouseOptions.queryOptions.database}.time_series` - const stream = ch.query(selectQuery) - // or collect records yourself - const rows = [] - stream.on('metadata', function (columns) { - // do something with column list - }) - stream.on('data', function (row) { - rows.push(row) - }) - stream.on('error', function (err) { - logger.error(err, 'Error reloading fingerprints') - }) - stream.on('end', function () { - rows.forEach(function (row) { - try { - const JSONLabels = toJSON(row[1]/*.replace(/\!?=/g, ':')*/) - labels.add(row[0], JSON.stringify(JSONLabels)) - for (const key in JSONLabels) { - // logger.debug('Adding key',row); - labels.add('_LABELS_', key) - labels.add(key, JSONLabels[key]) - } - } catch (err) { logger.error(err, 'error reloading fingerprints') } - }) - }) -} - -const fakeStats = { summary: { bytesProcessedPerSecond: 0, linesProcessedPerSecond: 0, totalBytesProcessed: 0, totalLinesProcessed: 0, execTime: 0.001301608 }, store: { totalChunksRef: 0, totalChunksDownloaded: 0, chunksDownloadTime: 0, headChunkBytes: 0, headChunkLines: 0, decompressedBytes: 0, decompressedLines: 0, compressedBytes: 0, totalDuplicates: 0 }, ingester: { totalReached: 1, totalChunksMatched: 0, totalBatches: 0, totalLinesSent: 0, headChunkBytes: 0, headChunkLines: 0, decompressedBytes: 0, decompressedLines: 0, compressedBytes: 0, totalDuplicates: 0 } } - -const scanFingerprints = async function (query) { - logger.debug('Scanning Fingerprints...') - const _query = transpiler.transpile(query) - _query.step = UTILS.parseDurationSecOrDefault(query.step, 5) * 1000 - _query.csv = query.csv - return queryFingerprintsScan(_query) -} - -const scanTempo = async function (query) { - return queryTempoScan(query) -} - -const instantQueryScan = async function (query) { - logger.debug('Scanning Fingerprints...') - const time = parseMs(query.time, Date.now()) - query.start = (time - 10 * 60 * 1000) * 1000000 - query.end = Date.now() * 1000000 - const _query = transpiler.transpile(query) - _query.step = UTILS.parseDurationSecOrDefault(query.step, 5) * 1000 - - const _stream = await axios.post(getClickhouseUrl() + '/', - _query.query + ' FORMAT JSONEachRow', - { - responseType: 'stream' - } - ) - const dataStream = preprocessStream(_stream, _query.stream || []) - const res = new Transform({ - transform (chunk, encoding, callback) { - callback(null, chunk) - } - }) - setTimeout(() => { - try { - _query.matrix ? outputQueryVector(dataStream, res) : outputQueryStreams(dataStream, res) - } catch (e) { logger.error(e) } - }, 0) - return res -} - -const tempoQueryScan = async function (query, res, traceId) { - const response = { - v2: [], - v1: [] - } - response.v2 = await tempoQueryScanV2(query, res, traceId) - return response -} - -const tempoQueryScanV2 = async function (query, res, traceId) { - logger.debug(`Scanning Tempo Fingerprints... ${traceId}`) - const _stream = await axios.post(getClickhouseUrl() + '/', - `SELECT payload_type, payload FROM ${DATABASE_NAME()}.tempo_traces${dist} WHERE oid='0' AND trace_id=unhex('${traceId}') ORDER BY timestamp_ns ASC LIMIT 2000 FORMAT JSONEachRow`, - { - responseType: 'stream' - } - ) - return await StringStream.from(_stream.data).lines().map((e) => { - try { - const _e = JSON.parse(e) - return { ..._e, payload: JSON.parse(_e.payload) } - } catch (e) { - return null - } - }, DataStream).filter(e => e).toArray() -} - -const tempoSearchScan = async function (query, res) { - logger.debug(`Scanning Tempo traces... ${query.tags}`) - const time = parseMs(query.time, Date.now()) - /* Tempo does not seem to pass start/stop parameters. Use ENV or default 24h */ - const hours = this.tempo_span || 24 - if (!query.start) query.start = (time - (hours * 60 * 60 * 1000)) * 1000000 - if (!query.end) query.end = Date.now() * 1000000 - const _query = transpiler.transpile(query) - _query.step = UTILS.parseDurationSecOrDefault(query.step, 5) * 1000 - - const _stream = await axios.post(getClickhouseUrl() + '/', - _query.query + ' FORMAT JSONEachRow', - { - responseType: 'stream' - } - ) - const dataStream = preprocessStream(_stream, _query.stream || []) - logger.info('debug tempo search', query) - return await (outputTempoSearch(dataStream, res)) -} - -/** - * - * @param traces {Object[]} openzipkin traces array see https://zipkin.io/zipkin-api/#/default/post_spans - * @returns {Promise<unknown>} - */ -function pushZipkin (traces) { - return new Promise((resolve, reject) => { - const id = getThrottlerId() - throttler.postMessage({ - type: 'traces', - data: traces.map(obj => (new Zipkin(obj)).toJson()).join('\n'), - id: id - }) - resolvers[id] = resolve - rejectors[id] = reject - }) -} - -/** - * - * @param traces {Object[]} openzipkin traces array see https://zipkin.io/zipkin-api/#/default/post_spans - * @returns {Promise<unknown>} - */ -function pushOTLP (traces) { - return new Promise((resolve, reject) => { - const id = getThrottlerId() - throttler.postMessage({ - type: 'traces', - data: traces.map(obj => (new Otlp(obj)).toJson()).join('\n'), - id: id - }) - resolvers[id] = resolve - rejectors[id] = reject - }) -} - -/** - * @param query {{ - * query: string, - * duration: number, - * matrix: boolean, - * stream: (function(DataStream): DataStream)[], - * step: number, - * csv?: boolean - * }} - * @returns {Promise<Readable>} - */ -const queryFingerprintsScan = async function (query) { - logger.debug('Scanning Fingerprints...') - - // logger.info(_query.query); - const _stream = await getClickhouseStream(query) - const dataStream = preprocessStream(_stream, query.stream || []) - const res = new Transform({ - transform (chunk, encoding, callback) { - callback(null, chunk) - } - }) - if (query.csv) { - setTimeout(async () => { - try { - await (query.matrix - ? outputQueryMatrixCSV(dataStream, res, query.step, query.duration) - : outputQueryStreamsCSV(dataStream, res)) - } catch (e) { logger.error(e) } - }, 0) - return res - } - setTimeout(async () => { - try { - await (query.matrix - ? outputQueryMatrix(dataStream, res, query.step, query.duration) - : outputQueryStreams(dataStream, res)) - } catch (e) { logger.error(e) } - }, 0) - return res -} - -/** - * @param query {{ - * query: string, - * duration: number, - * matrix: boolean, - * stream: (function(DataStream): DataStream)[], - * step: number, - * start: number, - * end: number, - * minDurationNs: number, - * maxDurationNs: number, - * tags: Object<string, string> - * }} - * @returns {Promise<{v1: Object[], v2: Object[]}>} - */ -const queryTempoScan = async function (query) { - const resp = { - v1: [], - v2: [] - } - resp.v2 = await queryTempoScanV2({ ...query }) - return resp -} - -const queryTempoScanV2 = async function (query) { - const select = `SELECT hex(trace_id) as traceID, service_name as rootServiceName, - name as rootTraceName, timestamp_ns as startTimeUnixNano, - intDiv(duration_ns, 1000000) as durationMs` - const from = `FROM ${DATABASE_NAME()}.tempo_traces${dist}` - const where = [ - 'oid = \'0\'', - `timestamp_ns >= ${parseInt(query.start)} AND timestamp_ns <= ${parseInt(query.end)}`, - (query.minDurationNs ? `duration_ns >= ${parseInt(query.minDurationNs)}` : null), - (query.maxDurationNs ? `duration_ns <= ${parseInt(query.maxDurationNs)}` : null) - ].filter(e => e) - let idxSubsel = null - if (query.tags) { - idxSubsel = Object.entries(query.tags) - .map(e => { - const timestampNs = query.limit ? ', timestamp_ns' : '' - let subQ = `SELECT trace_id, span_id ${timestampNs} FROM ${DATABASE_NAME()}.tempo_traces_attrs_gin WHERE oid='0'` + - ` AND date >= '${formatISO9075(query.start / 1000000).substring(0, 10)}' ` + - ` AND date <= '${formatISO9075(query.end / 1000000).substring(0, 10)}'` + - ` AND key = ${csql.quoteVal(e[0].toString())} AND val = ${csql.quoteVal(e[1].toString())}` + - ` AND timestamp_ns >= ${parseInt(query.start)} AND timestamp_ns <= ${parseInt(query.end)}` - if (query.minDurationNs) { - subQ += ` AND duration >= ${query.minDurationNs}` - } - if (query.maxDurationNs) { - subQ += ` AND duration <= ${query.maxDurationNs}` - } - return subQ - }).join(' INTERSECT ') - if (query.limit) { - idxSubsel = `SELECT trace_id, span_id FROM (${idxSubsel}) as rawsubsel ` + - `ORDER BY timestamp_ns DESC LIMIT ${parseInt(query.limit)}` - } - where.push(`(trace_id, span_id) IN (${idxSubsel})`) - } - const limit = query.limit ? `LIMIT ${parseInt(query.limit)}` : '' - const sql = `${select} ${from} WHERE ${where.join(' AND ')} ORDER BY timestamp_ns DESC ${limit} FORMAT JSON` - console.log(sql) - const resp = await rawRequest(sql, null, process.env.CLICKHOUSE_DB || 'cloki') - return resp.data.data ? resp.data.data : JSON.parse(resp.data).data -} - -async function queryTempoTags () { - const q = `SELECT distinct key - FROM ${DATABASE_NAME()}.tempo_traces_kv${dist} - WHERE oid='0' AND date >= toDate(NOW()) - interval '1 day' - FORMAT JSON` - const resp = await axios.post(getClickhouseUrl() + '/',q) - return resp.data.data ? resp.data.data : JSON.parse(resp.data).data -} - -/** - * - * @param tag {string} - * @returns {Promise<{val: string}[]>} - */ -async function queryTempoValues (tag) { - const q = `SELECT distinct val - FROM ${DATABASE_NAME()}.tempo_traces_kv${dist} - WHERE oid='0' AND date >= toDate(NOW()) - interval '1 day' AND key = ${csql.quoteVal(tag)} - FORMAT JSON` - const resp = await axios.post(getClickhouseUrl() + '/', q) - return resp.data.data ? resp.data.data : JSON.parse(resp.data).data -} - -/** - * - * @param query {{query: string}} - * @returns {Promise<Stream>} - */ -const getClickhouseStream = (query) => { - return axios.post(getClickhouseUrl() + '/', - query.query + ' FORMAT JSONEachRow', - { - responseType: 'stream' - } - ) -} - -/** - * - * @param dataStream {DataStream} - * @param res {{res: { - * write: (function(string)), - * onBegin: (function(string)), - * onEnd: (function(string)) - * }}} - * @param i {number} - * @returns {Promise<void>} - */ -const outputQueryStreams = async (dataStream, res, i) => { - //res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': CORS }) - const gen = dataStream.toGenerator() - i = i || 0 - let lastLabels = null - let lastStream = [] - res.onBegin - ? res.onBegin('{"status":"success", "data":{ "resultType": "streams", "result": [') - : res.write('{"status":"success", "data":{ "resultType": "streams", "result": [') - for await (const item of gen()) { - if (!item) { - continue - } - if (!item.labels) { - if (!lastLabels || !lastStream.length) { - continue - } - res.write(i ? ',' : '') - res.write(JSON.stringify({ - stream: parseLabels(lastLabels), - values: lastStream - })) - lastLabels = null - lastStream = [] - ++i - continue - } - const hash = hashLabels(item.labels) - const ts = item.timestamp_ns || null - if (hash === lastLabels) { - ts && lastStream.push([ts, item.string]) - continue - } - if (lastLabels) { - res.write(i ? ',' : '') - res.write(JSON.stringify({ - stream: parseLabels(lastLabels), - values: lastStream - })) - ++i - } - lastLabels = hash - lastStream = ts ? [[ts, item.string]] : [] - } - res.onEnd ? res.onEnd(']}}') : res.write(']}}') - res.end() -} - -/** - * - * @param dataStream {DataStream} - * @param res {{res: { - * write: (function(string)), - * onBegin: (function(string)), - * onEnd: (function(string)) - * }}} - * @param i {number} - * @returns {Promise<void>} - */ -const outputQueryStreamsCSV = async (dataStream, res, i) => { - //res.writeHead(200, { 'Content-Type': 'text/csv', 'Access-Control-Allow-Origin': CORS }) - const gen = dataStream.toGenerator() - const writer = createCsvArrayWriter({ - header: ['timestamp_ns', 'labels', 'string'] - }) - res.onBegin - ? res.onBegin(writer.getHeaderString()) - : res.write(writer.getHeaderString()) - for await (const item of gen()) { - if (!item) { - continue - } - const record = [ - item.timestamp_ns, - JSON.stringify(item.labels), - item.string - ] - res.write(writer.stringifyRecords([record])) - } - res.onEnd ? res.onEnd('') : res.write('') - res.end() -} - -/** - * - * @param dataStream {DataStream} - * @param res {{write: (function(string))}} - * @param stepMs {number} - * @param durationMs {number} - * @returns {Promise<void>} - */ -const outputQueryMatrix = async (dataStream, res, - stepMs, durationMs) => { - //res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': CORS }) - const addPoints = Math.ceil(durationMs / stepMs) - const gen = dataStream.toGenerator() - let i = 0 - let lastLabels = null - let lastStream = [] - let lastTsMs = 0 - res.write('{"status":"success", "data":{ "resultType": "matrix", "result": [') - for await (const item of gen()) { - if (!item) { - continue - } - if (!item.labels) { - if (!lastLabels || !lastStream.length) { - continue - } - res.write(i ? ',' : '') - res.write(JSON.stringify({ - metric: parseLabels(lastLabels), - values: lastStream - })) - lastLabels = null - lastStream = [] - lastTsMs = 0 - ++i - continue - } - const hash = hashLabels(item.labels) - const ts = item.timestamp_ns ? parseInt(item.timestamp_ns) : null - if (hash === lastLabels) { - if (ts < (lastTsMs + stepMs)) { - continue - } - for (let j = 0; j < addPoints; ++j) { - ts && lastStream.push([(ts + stepMs * j) / 1000, item.value.toString()]) - } - lastTsMs = ts - continue - } - if (lastLabels) { - res.write(i ? ',' : '') - res.write(JSON.stringify({ - metric: parseLabels(lastLabels), - values: lastStream - })) - ++i - } - lastLabels = hash - lastStream = [] - for (let j = 0; j < addPoints; ++j) { - ts && lastStream.push([(ts + stepMs * j) / 1000, item.value.toString()]) - } - lastTsMs = ts - } - res.write(']}}') - res.end() -} - -/** - * - * @param dataStream {DataStream} - * @param res {{write: (function(string))}} - * @param stepMs {number} - * @param durationMs {number} - * @returns {Promise<void>} - */ -const outputQueryMatrixCSV = async (dataStream, res, - stepMs, durationMs) => { - //res.writeHead(200, { 'Content-Type': 'text/csv', 'Access-Control-Allow-Origin': CORS }) - const addPoints = Math.ceil(durationMs / stepMs) - const gen = dataStream.toGenerator() - let lastTsMs = 0 - let hash = '' - - const writer = createCsvArrayWriter({ - header: ['timestamp_ns', 'labels', 'value'] - }) - res.onBegin - ? res.onBegin(writer.getHeaderString()) - : res.write(writer.getHeaderString()) - for await (const item of gen()) { - if (!item || !item.labels) { - continue - } - if (hashLabels(item.labels) !== hash) { - hash = hashLabels(item.labels) - lastTsMs = 0 - } - const ts = item.timestamp_ns ? parseInt(item.timestamp_ns) : null - if (ts < (lastTsMs + stepMs)) { - continue - } - for (let j = 0; j < addPoints; ++j) { - const record = [ - (ts + stepMs * j) * 1000000, - JSON.stringify(item.labels), - item.value.toString() - ] - ts && res.write(writer.stringifyRecords([record])) - lastTsMs = (ts + stepMs * j) - } - } - res.onEnd ? res.onEnd('') : res.write('') - res.end() -} - -/** - * - * @param dataStream {DataStream} - * @param res {Writable} - * @returns {Promise<void>} - */ -const outputQueryVector = async (dataStream, res) => { - //res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': CORS }) - const gen = dataStream.toGenerator() - let i = 0 - let lastLabels = null - let lastTsMs = 0 - let lastValue = 0 - res.write('{"status":"success", "data":{ "resultType": "vector", "result": [') - for await (const item of gen()) { - if (!item) { - continue - } - if (!item.labels) { - if (!lastLabels || !lastTsMs) { - continue - } - res.write(i ? ',' : '') - res.write(JSON.stringify({ - metric: parseLabels(lastLabels), - value: [lastTsMs / 1000, lastValue.toString()] - })) - lastLabels = null - lastTsMs = 0 - ++i - continue - } - const hash = hashLabels(item.labels) - const ts = item.timestamp_ns ? parseInt(item.timestamp_ns) : null - if (hash === lastLabels) { - lastTsMs = ts - lastValue = item.value - continue - } - if (lastLabels) { - res.write(i ? ',' : '') - res.write(JSON.stringify({ - metric: parseLabels(lastLabels), - value: [lastTsMs / 1000, lastValue.toString()] - })) - ++i - } - lastLabels = hash - lastTsMs = ts - lastValue = item.value - } - res.write(']}}') - res.end() -} - -/** - * - * @param dataStream {DataStream} - * @param res {{res: {write: (function(string)), writeHead: (function(number, {}))}}} - * @param traceId {String} - * @returns {Promise<any>} - */ -const outputTempoSpans = async (dataStream, res, traceId) => { - // res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': CORS }) - return dataStream.filter(e => e && e.string).map(e => { - try { - return JSON.parse(e.string) - } catch (e) { - return null - } - }, DataStream).filter(e => e).toArray() -} - -/** - * - * @param dataStream {DataStream} - * @param res {{res: {write: (function(string)), writeHead: (function(number, {}))}}} - * @returns {Promise<any>} - * - * {"traces": [{"traceID":"AC62F5E32AFE5C28D4F8DCA4C159627E","rootServiceName":"dummy-server","rootTraceName":"request_response","startTimeUnixNano":1661290946168377000,"durationMs":10}]} - * - */ -const outputTempoSearch = async (dataStream, res) => { - const gen = dataStream.toGenerator() - let i = 0 - let response = '{"traces": [' - for await (const item of gen()) { - if (!item || !item.string) { - continue - } - let duration = parseInt((item.Span.end_time_unix_nano - item.Span.start_time_unix_nano) / 1000000) || 0; - let trace = `{"traceID": ${item.Span.trace_id}, "rootServiceName": ${item.ServiceName}, "rootTraceName": ${item.Span.name}, "startTimeUnixNano": ${item.Span.start_time_unix_nano}, "durationMs": ${duration}}` - response += (i ? ',' : '') - response += trace - i++ - } - response += (']}') - return response -} - - -/** - * - * @param rawStream {any} Stream from axios response - * @param processors {(function(DataStream): DataStream)[] | undefined} - * @returns {DataStream} - */ -const preprocessStream = (rawStream, processors) => { - let dStream = StringStream.from(rawStream.data).lines().endWith(JSON.stringify({ EOF: true })) - .map(chunk => { - try { - return chunk ? JSON.parse(chunk) : ({}) - } catch (e) { - return {} - } - }, DataStream) - .map(chunk => { - try { - if (!chunk || !chunk.labels) { - return chunk - } - const labels = chunk.extra_labels - ? { ...parseLabels(chunk.labels), ...parseLabels(chunk.extra_labels) } - : parseLabels(chunk.labels) - return { ...chunk, labels: labels } - } catch (e) { - logger.info(chunk) - return chunk - } - }, DataStream) - if (processors && processors.length) { - processors.forEach(f => { - dStream = f(dStream) - }) - } - return dStream -} - -/** - * - * @param rawStream {any} Stream from axios response - * @param processors {(function(DataStream): DataStream)[] | undefined} - * @returns {DataStream} - */ -const preprocessLiveStream = (rawStream, processors) => { - let dStream = StringStream.from(rawStream.data).lines().endWith(JSON.stringify({ EOF: true })) - .map(chunk => chunk ? JSON.parse(chunk) : ({}), DataStream) - .filter(chunk => { - return chunk && (chunk.row || chunk.EOF) - }).map(chunk => ({ - ...(chunk.row || {}), - EOF: chunk.EOF - })) - .map(chunk => { - try { - if (!chunk || !chunk.labels) { - return chunk - } - const labels = chunk.extra_labels - ? { ...parseLabels(chunk.labels), ...parseLabels(chunk.extra_labels) } - : parseLabels(chunk.labels) - return { ...chunk, labels: labels } - } catch (e) { - logger.info(chunk) - return chunk - } - }, DataStream) - if (processors && processors.length) { - processors.forEach(f => { - dStream = f(dStream) - }) - } - return dStream -} - -/* Qryn Metrics Column */ -const scanMetricFingerprints = function (settings, client, params) { - logger.debug({ settings }, 'Scanning Clickhouse...') - // populate matrix structure - const resp = { - status: 'success', - data: { - resultType: 'matrix', - result: [] - } - } - // Check for required fields or return nothing! - if (!settings || !settings.table || !settings.db || !settings.tag || !settings.metric) { client.send(resp); return } - settings.interval = settings.interval ? parseInt(settings.interval) : 60 - if (!settings.timefield) settings.timefield = process.env.CLICKHOUSE_TIMEFIELD || 'record_datetime' - - const tags = settings.tag.split(',') - let template = 'SELECT ' + tags.join(', ') + ', groupArray((toUnixTimestamp(timestamp_ns)*1000, toString(value))) AS groupArr FROM (SELECT ' - if (tags) { - tags.forEach(function (tag) { - tag = tag.trim() - template += " visitParamExtractString(labels, '" + tag + "') as " + tag + ',' - }) - } - // if(settings.interval > 0){ - template += ' toStartOfInterval(toDateTime(timestamp_ns/1000), INTERVAL ' + settings.interval + ' second) as timestamp_ns, value' + - // } else { - // template += " timestampMs, value" - // } - - // template += " timestampMs, value" - ' FROM ' + settings.db + '.samples RIGHT JOIN ' + settings.db + '.time_series ON samples.fingerprint = time_series.fingerprint' - if (params.start && params.end) { - template += ' WHERE ' + settings.timefield + ' BETWEEN ' + parseInt(params.start / 1000000000) + ' AND ' + parseInt(params.end / 1000000000) - // template += " WHERE "+settings.timefield+" BETWEEN "+parseInt(params.start/1000000) +" AND "+parseInt(params.end/1000000) - } - if (tags) { - tags.forEach(function (tag) { - tag = tag.trim() - template += " AND (visitParamExtractString(labels, '" + tag + "') != '')" - }) - } - if (settings.where) { - template += ' AND ' + settings.where - } - template += ' AND value > 0 ORDER BY timestamp_ns) GROUP BY ' + tags.join(', ') - - const stream = ch.query(template) - // or collect records yourself - const rows = [] - stream.on('metadata', function (columns) { - // do something with column list - }) - stream.on('data', function (row) { - rows.push(row) - }) - stream.on('error', function (err) { - // TODO: handler error - client.code(400).send(err) - }) - stream.on('end', function () { - logger.debug({ rows }, 'CLICKHOUSE RESPONSE') - if (!rows || rows.length < 1) { - resp.data.result = [] - resp.data.stats = fakeStats - } else { - try { - rows.forEach(function (row) { - const metrics = { metric: {}, values: [] } - const tags = settings.tag.split(',') - // bypass empty blocks - if (row[row.length - 1].length < 1) return - // iterate tags - for (let i = 0; i < row.length - 1; i++) { - metrics.metric[tags[i]] = row[i] - } - // iterate values - row[row.length - 1].forEach(function (row) { - if (row[1] === 0) return - metrics.values.push([parseInt(row[0] / 1000), row[1].toString()]) - }) - resp.data.result.push(metrics) - }) - } catch (err) { logger.error(err, 'Error scanning fingerprints') } - } - logger.debug({ resp }, 'QRYN RESPONSE') - client.send(resp) - }) -} - -/** - * Clickhouse Metrics Column Query - * @param settings {{ - * db: string, - * table: string, - * interval: string | number, - * tag: string, - * metric: string - * }} - * @param client {{ - * code: function(number): any, - * send: function(string): any - * }} - * @param params {{ - * start: string | number, - * end: string | number, - * shift: number | undefined - * }} - */ -const scanClickhouse = function (settings, client, params) { - logger.debug('Scanning Clickhouse...', settings) - - // populate matrix structure - const resp = { - status: 'success', - data: { - resultType: 'matrix', - result: [] - } - } - - // TODO: Replace this template with a proper parser! - // Check for required fields or return nothing! - if (!settings || !settings.table || !settings.db || !settings.tag || !settings.metric) { client.send(resp); return } - settings.interval = settings.interval ? parseInt(settings.interval) : 60 - // Normalize timefield - if (!settings.timefield) settings.timefield = process.env.TIMEFIELD || 'record_datetime' - else if (settings.timefield === 'false') settings.timefield = false - // Normalize Tags - if (settings.tag.includes('|')) { settings.tag = settings.tag.split('|').join(',') } - // Lets query! - let template = 'SELECT ' + settings.tag + ', groupArray((t, c)) AS groupArr FROM (' - // Check for timefield or Bypass timefield - if (settings.timefield) { - const shiftSec = params.shift ? params.shift / 1000 : 0 - const timeReq = params.shift - ? `intDiv(toUInt32(${settings.timefield} - ${shiftSec}), ${settings.interval}) * ${settings.interval} + ${shiftSec}` - : 'intDiv(toUInt32(' + settings.timefield + '), ' + settings.interval + ') * ' + settings.interval - template += `SELECT (${timeReq}) * 1000 AS t, ` + settings.tag + ', ' + settings.metric + ' c ' - } else { - template += 'SELECT toUnixTimestamp(now()) * 1000 AS t, ' + settings.tag + ', ' + settings.metric + ' c ' - } - template += 'FROM ' + settings.db + '.' + settings.table - // Check for timefield or standalone where conditions - if (params.start && params.end && settings.timefield) { - template += ' PREWHERE ' + settings.timefield + ' BETWEEN ' + parseInt(params.start / 1000000000) + ' AND ' + parseInt(params.end / 1000000000) - if (settings.where) { - template += ' AND ' + settings.where - } - } else if (settings.where) { - template += ' WHERE ' + settings.where - } - template += ' GROUP BY t, ' + settings.tag + ' ORDER BY t, ' + settings.tag + ')' - template += ' GROUP BY ' + settings.tag + ' ORDER BY ' + settings.tag - // Read-Only: Initiate a new driver connection - if (boolEnv('READONLY')) { - const tmp = { ...clickhouseOptions, queryOptions: { database: settings.db } } - ch = new ClickHouse(tmp) - } - - const stream = ch.query(template) - // or collect records yourself - const rows = [] - stream.on('metadata', function (columns) { - // do something with column list - }) - stream.on('data', function (row) { - rows.push(row) - }) - stream.on('error', function (err) { - // TODO: handler error - logger.error(err, 'error scanning clickhouse') - resp.status = "error" - resp.data.result = [] - client.send(resp) - }) - stream.on('end', function () { - logger.debug({ rows }, 'CLICKHOUSE RESPONSE') - if (!rows || rows.length < 1) { - resp.data.result = [] - resp.data.stats = fakeStats - } else { - try { - rows.forEach(function (row) { - const metrics = { metric: {}, values: [] } - const tags = settings.tag.split(',').map(t => t.trim()) - // bypass empty blocks - if (row[row.length - 1].length < 1) return - // iterate tags - for (let i = 0; i < row.length - 1; i++) { - metrics.metric[tags[i]] = row[i] - } - // iterate values - row[row.length - 1].forEach(function (row) { - if (row[1] === 0) return - metrics.values.push([parseInt(row[0] / 1000), row[1].toString()]) - }) - resp.data.result.push(metrics) - }) - } catch (err) { logger.error(err, 'error scanning clickhouse') } - } - logger.debug({ resp }, 'QRYN RESPONSE') - client.send(resp) - }) -} - -/** - * - * @param matches {string[]} ['{ts1="a1"}', '{ts2="a2"}', ...] - * @param res {{res: {write: (function(string)), writeHead: (function(number, {}))}}} - */ -const getSeries = async (matches) => { - const query = transpiler.transpileSeries(matches) - const stream = await rawRequest(query + ' FORMAT JSONEachRow', null, DATABASE_NAME(), { - responseType: 'stream' - }) - const res = new Transform({ - transform (chunk, encoding, callback) { - callback(null, chunk) - } - }) - res.write('{"status":"success", "data":[', 'utf-8') - let lastString = '' - let i = 0 - let lastData = 0 - let open = true - stream.data.on('data', (chunk) => { - lastData = Date.now() - const strChunk = Buffer.from(chunk).toString('utf-8') - const lines = (lastString + strChunk).split('\n') - lastString = lines.pop() - lines.forEach(line => { - if (!line) { - return - } - try { - const obj = JSON.parse(line) - if (obj.labels) { - res.write((i === 0 ? '' : ',') + obj.labels) - ++i - } - } catch (err) { - logger.error({ line: line, err }, 'Error parsing line') - } - }) - }) - const close = () => { - if (lastString) { - res.write((i === 0 ? '' : ',') + lastString) - } - res.end(']}') - open = false - } - const maybeClose = () => { - if (open && Date.now() - lastData >= 10000) { - close() - } - if (open && Date.now() - lastData < 10000) { - setTimeout(maybeClose, 10000) - } - } - setTimeout(maybeClose, 10000) - stream.data.on('end', close) - stream.data.on('error', close) - stream.data.on('finish', close) - return res -} - -const ping = async () => { - await Promise.all([ - new Promise((resolve, reject) => ch.query('SELECT 1', undefined, (err) => { - if (err) { - logger.error(err) - } - err ? reject(err) : resolve(err) - })), - (async function () { - try { - await axios.get(`${getClickhouseUrl()}/?query=SELECT 1`) - } catch (e) { - logger.error(e) - } - })() - ]) -} - -/* Module Exports */ - -/** - * - * @param name {string} - * @param request {string} - * @param options {{db : string | undefined, timeout_sec: number | undefined}} - */ -module.exports.createLiveView = (name, request, options) => { - const db = options.db || clickhouseOptions.queryOptions.database - const timeout = options.timeout_sec ? `WITH TIMEOUT ${options.timeout_sec}` : '' - return axios.post(`${getClickhouseUrl()}/?allow_experimental_live_view=1`, - `CREATE LIVE VIEW ${db}.${name} ${timeout} AS ${request}`) -} - -/** - * - * @param db {string} - * @param name {string} - * @param name {string} - * @param res {{res: {write: (function(string)), writeHead: (function(number, {}))}}} - * @param options {{ - * stream: (function(DataStream): DataStream)[], - * }} - * @returns Promise<[Promise<void>, CancelTokenSource]> - */ -module.exports.watchLiveView = async (name, db, res, options) => { - db = db || clickhouseOptions.queryOptions.database - const cancel = axios.CancelToken.source() - const stream = await axios.post(`${getClickhouseUrl()}/?allow_experimental_live_view=1`, - `WATCH ${db}.${name} FORMAT JSONEachRowWithProgress`, - { - responseType: 'stream', - cancelToken: cancel.token - }) - let buffer = [] - let lastString = [] - stream.data.on('data', /** @param data {Buffer} */data => { - const lastNewline = data.lastIndexOf('\n') - if (lastNewline === -1) { - lastString.push(data) - return - } - buffer.push(...lastString) - buffer.push(data.slice(0, lastNewline + 1)) - lastString = [data.slice(lastNewline + 1)] - }) - const flush = async () => { - const _buffer = new Blob(buffer) - buffer = [] - const _stream = preprocessLiveStream({ data: await _buffer.text() }, options.stream) - const gen = _stream.toGenerator() - for await (const item of gen()) { - if (!item || !item.labels) { - continue - } - res.res.write(item) - } - } - - let flushing = false - const flushTimer = setInterval(async () => { - if (flushing) { - return - } - try { - flushing = true - if (!buffer.length) { - return - } - await flush() - } finally { - flushing = false - } - }, 500) - - const endPromise = new Promise(resolve => { - stream.data.on('end', () => { - clearInterval(flushTimer) - resolve() - }) - stream.data.on('close', () => { - clearInterval(flushTimer) - resolve() - }) - stream.data.on('error', () => { - clearInterval(flushTimer) - resolve() - }) - }) - - /*const endPromise = (async () => { - const _stream = preprocessLiveStream(stream, options.stream) - const gen = _stream.toGenerator() - res.res.writeHead(200, {}) - for await (const item of gen()) { - if (!item || !item.labels) { - continue - } - res.res.write(item) - } - res.res.end() - })()*/ - return [endPromise, cancel] -} - -module.exports.createMV = async (query, id, url) => { - const request = `CREATE MATERIALIZED VIEW ${clickhouseOptions.queryOptions.database}.${id} ` + - `ENGINE = URL('${url}', JSON) AS ${query}` - logger.info(`MV: ${request}`) - await axios.post(`${getClickhouseUrl()}`, request) -} - -const samplesReadTable = { - checked: false, - v1: false, - v1Time: false, - versions: {}, - getName: (fromMs) => { - if (!samplesReadTable.checked) { - return 'samples_read_v2_2' - } - if (!samplesReadTable.v1) { - return 'samples_v3' - } - if (!fromMs || BigInt(fromMs + '000000') < samplesReadTable.v1Time) { - return 'samples_read_v2_2' - } - return 'samples_v3' - }, - check: async function () { - await this.settingsVersions() - await this._check('samples_v2') - if (samplesReadTable.v1) { - return - } - await this._check('samples') - }, - checkVersion: function (ver, fromMs) { - return samplesReadTable.versions[ver] < fromMs - }, - _check: async function (tableName) { - try { - logger.info('checking old samples support: ' + tableName) - samplesReadTable.checked = true - const tablesResp = await axios.post(`${getClickhouseUrl()}/?database=${UTILS.DATABASE_NAME()}`, - 'show tables format JSON') - samplesReadTable.v1 = tablesResp.data.data.find(row => row.name === tableName) - if (!samplesReadTable.v1) { - return - } - logger.info('checking last timestamp') - const v1EndTime = await axios.post(`${getClickhouseUrl()}/?database=${UTILS.DATABASE_NAME()}`, - `SELECT max(timestamp_ns) as ts FROM ${UTILS.DATABASE_NAME()}.${tableName} format JSON`) - if (!v1EndTime.data.rows) { - samplesReadTable.v1 = false - return - } - samplesReadTable.v1 = true - samplesReadTable.v1Time = BigInt(v1EndTime.data.data[0].ts) - logger.warn('!!!WARNING!!! You use Qryn in the backwards compatibility mode! Some requests can be less efficient and cause OOM errors. To finish migration please look here: https://github.com/metrico/qryn/wiki/Upgrade') - } catch (e) { - logger.error(e.message) - logger.error(e.stack) - samplesReadTable.v1 = false - logger.info('old samples table not supported') - } finally { - UTILS.onSamplesReadTableName(samplesReadTable.getName) - } - }, - settingsVersions: async function () { - const versions = await rawRequest( - `SELECT argMax(name, inserted_at) as _name, argMax(value, inserted_at) as _value - FROM ${UTILS.DATABASE_NAME()}.settings${dist} WHERE type == 'update' GROUP BY fingerprint HAVING _name != '' FORMAT JSON`, - null, - UTILS.DATABASE_NAME() - ) - for (const version of versions.data.data) { - this.versions[version._name] = parseInt(version._value) * 1000 - } - UTILS.onCheckVersion(samplesReadTable.checkVersion) - } - -} - -/** - * - * @param query {string} - * @param data {string | Buffer | Uint8Array} - * @param database {string} - * @param config {Object?} - * @returns {Promise<AxiosResponse<any>>} - */ -const rawRequest = async (query, data, database, config) => { - try { - if (data && !(Buffer.isBuffer(data) || data instanceof Uint8Array || typeof data === 'string')) { - throw new Error('data must be Buffer, Uint8Array or String: currently the data is: ' + typeof data) - } - if (typeof data === 'string') { - data = Buffer.from(data, 'utf8') - } - if (typeof query !== 'string') { - throw new Error('query must be String: currently the query is: ' + typeof query) - } - const getParams = [ - (database ? `database=${encodeURIComponent(database)}` : null), - (data ? `query=${encodeURIComponent(query)}` : null) - ].filter(p => p) - const url = `${getClickhouseUrl()}/${getParams.length ? `?${getParams.join('&')}` : ''}` - config = { - ...(config || {}), - method: 'post', - url: url, - data: data || query - } - return await axios(config) - } catch (e) { - logger.error('rawRequest error: ' + query) - e.response?.data && logger.error(e.response.data.toString()) - throw e - } -} - -/** - * - * @param names {{type: string, name: string}[]} - * @param database {string} - * @returns {Promise<Object<string, string>>} - */ -const getSettings = async (names, database) => { - const fps = names.map(n => UTILS.fingerPrint(JSON.stringify({ type: n.type, name: n.name }), false, - 'short-hash')) - const settings = await rawRequest(`SELECT argMax(name, inserted_at) as _name, - argMax(value, inserted_at) as _value - FROM ${database}.settings${dist} WHERE fingerprint IN (${fps.join(',')}) GROUP BY fingerprint HAVING _name != '' FORMAT JSON`, - null, database) - return settings.data.data.reduce((sum, cur) => { - sum[cur._name] = cur._value - return sum - }, {}) -} - -/** - * - * @param type {string} - * @param name {string} - * @param value {string} - * @param database {string} - * @returns {Promise<void>} - */ -const addSetting = async (type, name, value, database) => { - const fp = UTILS.fingerPrint(JSON.stringify({ type: type, name: name }), false, 'short-hash') - return rawRequest(`INSERT INTO ${UTILS.DATABASE_NAME()}.settings (fingerprint, type, name, value, inserted_at) FORMAT JSONEachRow`, - JSON.stringify({ - fingerprint: fp, - type: type, - name: name, - value: value, - inserted_at: formatISO9075(new Date()) - }) + '\n', database) -} - -module.exports.samplesReadTable = samplesReadTable -module.exports.databaseOptions = clickhouseOptions -module.exports.database = clickhouse -module.exports.cache = { bulk: bulk, bulk_labels: bulkLabels, labels: labels } -module.exports.scanFingerprints = scanFingerprints -module.exports.queryFingerprintsScan = queryFingerprintsScan -module.exports.instantQueryScan = instantQueryScan -module.exports.tempoQueryScan = tempoQueryScan -module.exports.tempoSearchScan = tempoSearchScan -module.exports.scanMetricFingerprints = scanMetricFingerprints -module.exports.scanClickhouse = scanClickhouse -module.exports.reloadFingerprints = reloadFingerprints -module.exports.init = initialize -module.exports.preprocessStream = preprocessStream -module.exports.capabilities = capabilities -module.exports.ping = ping -module.exports.stop = () => { - throttler.postMessage({ type: 'end' }) - throttler.removeAllListeners('message') - throttler.terminate() -} -module.exports.ready = () => state === 'READY' -module.exports.scanSeries = getSeries -module.exports.outputQueryStreams = outputQueryStreams -module.exports.samplesTableName = samplesTableName -module.exports.samplesReadTableName = samplesReadTableName -module.exports.getClickhouseUrl = getClickhouseUrl -module.exports.getClickhouseStream = getClickhouseStream -module.exports.preprocessLiveStream = preprocessLiveStream -module.exports.rawRequest = rawRequest -module.exports.getSettings = getSettings -module.exports.addSetting = addSetting -module.exports.scanTempo = scanTempo -module.exports.pushZipkin = pushZipkin -module.exports.queryTempoTags = queryTempoTags -module.exports.queryTempoValues = queryTempoValues -module.exports.pushOTLP = pushOTLP -module.exports.checkDB = checkDB diff --git a/lib/db/clickhouse_alerting.js b/lib/db/clickhouse_alerting.js deleted file mode 100644 index f2b0765a..00000000 --- a/lib/db/clickhouse_alerting.js +++ /dev/null @@ -1,332 +0,0 @@ -/** - * ALERT RULES - */ - -const axios = require('axios') -const { DATABASE_NAME } = require('../utils') -const UTILS = require('../utils') -const { getClickhouseUrl, rawRequest } = require('./clickhouse') -const Sql = require('@cloki/clickhouse-sql') -const { clusterName } = require('../../common') -const onCluster = clusterName ? `ON CLUSTER ${clusterName}` : '' -const dist = clusterName ? '_dist' : '' -/** - * @param ns {string} - * @param group {string} - * @param name {string} - * @returns {Promise<undefined|alerting.rule>} - */ -module.exports.getAlertRule = async (ns, group, name) => { - const fp = getRuleFP(ns, group, name) - const mark = Math.random() - const res = await rawRequest( - 'SELECT fingerprint, argMax(name, inserted_at) as name, argMax(value, inserted_at) as value ' + - `FROM ${DATABASE_NAME()}.settings${dist} ` + - `WHERE fingerprint = ${fp} AND ${mark} == ${mark} ` + - 'GROUP BY fingerprint ' + - 'HAVING name != \'\' ' + - 'FORMAT JSON', null, DATABASE_NAME()) - if (!res.data.data.length) { - return undefined - } - const rule = JSON.parse(res.data.data[0].value) - return rule -} - -/** - * - * @param namespace {string} - * @param group {alerting.group} - * @param rule {alerting.rule} - * @returns {Promise<undefined>} - */ -module.exports.putAlertRule = async (namespace, group, rule) => { - const ruleName = JSON.stringify({ type: 'alert_rule', ns: namespace, group: group.name, rule: rule.alert }) - const ruleFp = getRuleFP(namespace, group.name, rule.alert) - const ruleVal = { ...rule } - delete ruleVal._watcher - const groupName = JSON.stringify({ type: 'alert_group', ns: namespace, group: group.name }) - const groupFp = getGroupFp(namespace, group.name) - const groupVal = JSON.stringify({ name: group.name, interval: group.interval }) - await rawRequest( - `INSERT INTO ${DATABASE_NAME()}.settings${dist} (fingerprint, type, name, value, inserted_at) FORMAT JSONEachRow`, - JSON.stringify({ fingerprint: ruleFp, type: 'alert_rule', name: ruleName, value: JSON.stringify(ruleVal), inserted_at: Date.now() * 1000000 }) + '\n' + - JSON.stringify({ fingerprint: groupFp, type: 'alert_group', name: groupName, value: groupVal, inserted_at: Date.now() * 1000000 }), - DATABASE_NAME() - ) -} - -/** - * @param ns {string} - * @param group {string} - * @param rule {string} - * @param id {number} - * @return {Promise<number>} - */ -module.exports.getLastCheck = async (ns, group, rule, id) => { - const fp = getRuleFP(ns, group, rule) - id = id || 0 - const resp = await rawRequest( - `SELECT max(mark) as maxmark FROM ${DATABASE_NAME()}._alert_view_${fp}_mark WHERE id = ${id} FORMAT JSON`, - null, DATABASE_NAME() - ) - if (!resp.data.data[0]) { - return 0 - } - return resp.data.data[0].maxmark -} - -/** - * - * @param ns {string} - * @param group {string} - * @param rule {string} - * @param timeMs {number} - * @returns {Promise<void>} - */ -module.exports.activeSince = async (ns, group, rule, timeMs) => { - const fp = getRuleFP(ns, group, rule) - await axios.post(getClickhouseUrl(), - `INSERT INTO ${DATABASE_NAME()}._alert_view_${fp}_mark (id ,mark) (1, ${timeMs})` - ) -} - -/** - * @see alerting.d.ts - * @param limit {number | undefined} - * @param offset {number | undefined} - * @returns {Promise<[{rule: alerting.rule,name: alerting.ruleName}]>} - */ -module.exports.getAlertRules = async (limit, offset) => { - const _limit = limit ? `LIMIT ${limit}` : '' - const _offset = offset ? `OFFSET ${offset}` : '' - const mark = Math.random() - const res = await rawRequest( - 'SELECT fingerprint, argMax(name, inserted_at) as name, argMax(value, inserted_at) as value ' + - `FROM ${DATABASE_NAME()}.settings${dist} ` + - `WHERE type == 'alert_rule' AND ${mark} == ${mark} ` + - `GROUP BY fingerprint HAVING name != '' ORDER BY name ${_limit} ${_offset} FORMAT JSON`, - null, DATABASE_NAME()) - return res.data.data.map(e => { - return { rule: JSON.parse(e.value), name: JSON.parse(e.name) } - }) -} - -/** - * - * @param limit {number | undefined} - * @param offset {number | undefined} - * @returns {Promise<[{group: alerting.group, name: alerting.groupName}]>} - */ -module.exports.getAlertGroups = async (limit, offset) => { - const _limit = limit ? `LIMIT ${limit}` : '' - const _offset = offset ? `OFFSET ${offset}` : '' - const mark = Math.random() - const res = await axios.post(getClickhouseUrl(), - 'SELECT fingerprint, argMax(name, inserted_at) as name, argMax(value, inserted_at) as value ' + - `FROM ${DATABASE_NAME()}.settings${dist} ` + - `WHERE type == 'alert_group' AND ${mark} == ${mark} ` + - `GROUP BY fingerprint HAVING name != '' ORDER BY name ${_limit} ${_offset} FORMAT JSON`) - return res.data.data.map(e => { - return { group: JSON.parse(e.value), name: JSON.parse(e.name) } - }) -} - -/** - * @returns {Promise<number>} - */ -module.exports.getAlertRulesCount = async () => { - const mark = Math.random() - const res = await axios.post(getClickhouseUrl(), - 'SELECT COUNT(1) as count FROM (SELECT fingerprint ' + - `FROM ${DATABASE_NAME()}.settings${dist} ` + - `WHERE type=\'alert_rule\' AND ${mark} == ${mark} ` + - 'GROUP BY fingerprint ' + - 'HAVING argMax(name, inserted_at) != \'\') FORMAT JSON') - return parseInt(res.data.data[0].count) -} - -/** - * @param ns {string} - * @param group {string} - * @param rule {string} - * @returns {Promise<undefined>} - */ -module.exports.deleteAlertRule = async (ns, group, rule) => { - const fp = getRuleFP(ns, group, rule) - await axios.post(getClickhouseUrl(), - `INSERT INTO ${DATABASE_NAME()}.settings (fingerprint, type, name, value, inserted_at) FORMAT JSONEachRow\n` + - JSON.stringify({ fingerprint: fp, type: 'alert_rule', name: '', value: '', inserted_at: Date.now() }) - ) - const settings = clusterName ? '/?allow_nondeterministic_mutations=1&mutations_execute_nondeterministic_on_initiator=1' : '' - await axios.post(getClickhouseUrl() + settings, - `ALTER TABLE ${DATABASE_NAME()}.settings ${onCluster} DELETE WHERE fingerprint=${fp} AND inserted_at <= now64(9, 'UTC')` - ) -} - -/** - * @param ns {string} - * @param group {string} - * @return {Promise<void>} - */ -module.exports.deleteGroup = async (ns, group) => { - const fp = getGroupFp(ns, group) - await axios.post(getClickhouseUrl(), - `INSERT INTO ${DATABASE_NAME()}.settings${dist} (fingerprint, type, name, value, inserted_at) FORMAT JSONEachRow\n` + - JSON.stringify({ fingerprint: fp, type: 'alert_group', name: '', value: '', inserted_at: Date.now() }) - ) - const settings = clusterName ? '/?allow_nondeterministic_mutations=1&mutations_execute_nondeterministic_on_initiator=1' : '' - await axios.post(getClickhouseUrl() + settings, - `ALTER TABLE ${DATABASE_NAME()}.settings ${onCluster} DELETE WHERE fingerprint=${fp} AND inserted_at <= now64(9, 'UTC')` - ) -} - -/** - * @param ns {string} - * @param group {string} - * @param rule {string} - * @returns {Promise<void>} - */ -module.exports.dropAlertViews = async (ns, group, rule) => { - const fp = getRuleFP(ns, group, rule) - await axios.post(getClickhouseUrl(), - `DROP VIEW IF EXISTS ${DATABASE_NAME()}._alert_view_${fp} ${onCluster}`) - await axios.post(getClickhouseUrl(), - `DROP TABLE IF EXISTS ${DATABASE_NAME()}._alert_view_${fp}_mark ${onCluster}`) - await axios.post(getClickhouseUrl(), - `DROP TABLE IF EXISTS ${DATABASE_NAME()}._alert_view_${fp}_mark_dist ${onCluster}`) -} - -/** - * @param ns {string} - * @param group {string} - * @param rule {string} - * @returns {Promise<void>} - */ -module.exports.createMarksTable = async (ns, group, rule) => { - const fp = getRuleFP(ns, group, rule) - await axios.post(getClickhouseUrl(), - `CREATE TABLE IF NOT EXISTS ${DATABASE_NAME()}._alert_view_${fp}_mark ${onCluster}` + - '(id UInt8 default 0,mark UInt64, inserted_at DateTime default now()) ' + - `ENGINE ${clusterName ? 'Replicated' : ''}ReplacingMergeTree(mark) ORDER BY id`) - if (clusterName) { - await axios.post(getClickhouseUrl(), - `CREATE TABLE IF NOT EXISTS ${DATABASE_NAME()}._alert_view_${fp}_mark_dist ${onCluster}` + - '(id UInt8 default 0,mark UInt64, inserted_at DateTime default now()) ' + - `ENGINE=Distributed('${clusterName}', '${DATABASE_NAME()}', '_alert_view_${fp}_mark', id)`) - } -} - -/** - * @param ns {string} - * @param group {string} - * @param rule {string} - * @param request {Select} - * @returns {Promise<void>} - */ -module.exports.createAlertViews = async (ns, group, rule, request) => { - const fp = getRuleFP(ns, group, rule) - request.select( - [ - new Sql.Raw(`coalesce((SELECT max(mark) FROM ${DATABASE_NAME()}._alert_view_${fp}_mark WHERE id = 0), 0)`), - 'mark' - ] - ) - if (request.withs.str_sel) { - request.withs.str_sel.inline = true - } - if (request.withs.idx_sel) { - request.withs.idx_sel.inline = true - } - const strRequest = request.toString() - await module.exports.createMarksTable(ns, group, rule, request) - await axios.post(getClickhouseUrl(), - `INSERT INTO ${DATABASE_NAME()}._alert_view_${fp}_mark (mark) VALUES (${Date.now()})`) - await axios.post(getClickhouseUrl(), - `CREATE MATERIALIZED VIEW IF NOT EXISTS ${DATABASE_NAME()}._alert_view_${fp} ` + - `ENGINE=MergeTree() ORDER BY timestamp_ns PARTITION BY mark AS (${strRequest})`) -} - -module.exports.getLastMark = async (ns, group, rule) => { - const fp = getRuleFP(ns, group, rule) - const mark = await axios.post(getClickhouseUrl(), - `SELECT max(mark) as mark FROM ${DATABASE_NAME()}._alert_view_${fp}_mark${dist} WHERE id = 0 FORMAT JSON`) - return parseInt(mark.data.data[0].mark) -} - -/** - * @param ns {string} - * @param group {string} - * @param rule {string} - * @param newMark {number} - * @param id {number} - * @return {Promise<[number, number]>} old mark and new mark - */ -module.exports.incAlertMark = async (ns, group, rule, newMark, id) => { - const fp = getRuleFP(ns, group, rule) - const mark = await module.exports.getLastMark(ns, group, rule) - newMark = newMark || Date.now() - id = id || 0 - await axios.post(getClickhouseUrl(), - `INSERT INTO ${DATABASE_NAME()}._alert_view_${fp}_mark${dist} (mark, id) VALUES (${newMark}, ${id})`) - return [mark, newMark] -} - -/** - * @param ns {string} - * @param group {string} - * @param rule {string} - * @param mark {number} - * @return {Promise<*>} - */ -module.exports.getAlerts = async (ns, group, rule, mark) => { - const fp = getRuleFP(ns, group, rule) - const lastMsg = await axios.post(getClickhouseUrl(), - `SELECT * FROM ${DATABASE_NAME()}._alert_view_${fp} WHERE mark <= ${mark} ORDER BY timestamp_ns DESC FORMAT JSON`) - if (!lastMsg.data.data || !lastMsg.data.data.length) { - return undefined - } - return lastMsg.data.data -} - -/** - * - * @param ns {string} - * @param group {string} - * @param rule {string} - * @param mark {number} - * @returns {Promise<void>} - */ -module.exports.dropOutdatedParts = async (ns, group, rule, mark) => { - const fp = getRuleFP(ns, group, rule) - const partitions = await axios.post(getClickhouseUrl(), - `SELECT DISTINCT mark FROM ${DATABASE_NAME()}._alert_view_${fp}${dist} WHERE mark <= ${mark} FORMAT JSON`) - if (!partitions.data || !partitions.data.data || !partitions.data.data.length) { - return - } - for (const partid of partitions.data.data) { - await axios.post(getClickhouseUrl(), - `ALTER TABLE ${DATABASE_NAME()}._alert_view_${fp} DROP PARTITION tuple(${partid.mark})`) - } -} - -/** - * @param ns {string} - * @param group {string} - * @param rule {string} - * @returns {number} - */ -const getRuleFP = (ns, group, rule) => { - const ruleName = JSON.stringify({ type: 'alert_rule', ns: ns, group: group, rule: rule }) - const ruleFp = UTILS.fingerPrint(ruleName, false, 'short-hash') - return ruleFp -} -/** - * @param ns {string} - * @param group {string} - */ -const getGroupFp = (ns, group) => { - const groupName = JSON.stringify({ type: 'alert_group', ns: ns, group: group }) - const groupFp = UTILS.fingerPrint(groupName, false, 'short-hash') - return groupFp -} diff --git a/lib/db/clickhouse_options.js b/lib/db/clickhouse_options.js deleted file mode 100644 index 76a15bc4..00000000 --- a/lib/db/clickhouse_options.js +++ /dev/null @@ -1,22 +0,0 @@ -const UTILS = require('../utils') -const { samplesTableName, samplesReadTableName } = UTILS -const { boolEnv } = require('../../common') -const clickhouseOptions = { - host: process.env.CLICKHOUSE_SERVER || 'localhost', - port: process.env.CLICKHOUSE_PORT || 8123, - auth: process.env.CLICKHOUSE_AUTH || 'default:', - protocol: process.env.CLICKHOUSE_PROTO ? process.env.CLICKHOUSE_PROTO + ':' : 'http:', - readonly: boolEnv('READONLY'), - queryOptions: { database: process.env.CLICKHOUSE_DB || 'cloki' } -} - -function getClickhouseUrl () { - return `${clickhouseOptions.protocol}//${clickhouseOptions.auth}@${clickhouseOptions.host}:${clickhouseOptions.port}` -} - -module.exports = { - samplesTableName, - samplesReadTableName, - getClickhouseUrl, - databaseOptions: clickhouseOptions -} diff --git a/lib/db/maintain/index.js b/lib/db/maintain/index.js deleted file mode 100644 index 9330d327..00000000 --- a/lib/db/maintain/index.js +++ /dev/null @@ -1,200 +0,0 @@ -const hb = require('handlebars') -const client = require('../clickhouse') -const logger = require('../../logger') -const { samplesOrderingRule, clusterName } = require('../../../common') -const scripts = require('./scripts') -const getEnv = () => { - return { - CLICKHOUSE_DB: 'cloki', - LABELS_DAYS: 7, - SAMPLES_DAYS: 7, - ...process.env - } -} - -/** - * - * @param db {{name: string, storage_policy: string, skip_unavailable_shards: boolean}} - * @returns {Promise<void>} - */ -module.exports.upgrade = async (db) => { - await upgradeSingle(db.name, 1, scripts.overall, db.storage_policy, false) - await upgradeSingle(db.name, 2, scripts.traces, db.storage_policy, false) - await upgradeSingle(db.name, 5, scripts.profiles, db.storage_policy, false) - if (db.storage_policy) { - await client.addSetting('rotate', 'v3_storage_policy', db.storage_policy, db.name) - await client.addSetting('rotate', 'v1_traces_storage_policy', db.storage_policy, db.name) - } - if (clusterName) { - await upgradeSingle(db.name, 3, scripts.overall_dist, db.storage_policy, db.skip_unavailable_shards) - await upgradeSingle(db.name, 4, scripts.traces_dist, db.storage_policy, db.skip_unavailable_shards) - await upgradeSingle(db.name, 6, scripts.profiles_dist, db.storage_policy, db.skip_unavailable_shards) - } -} - -let isDBCreated = false -/** - * - * @param db {string} - * @param key {number} - * @param scripts {string[]} - * @param storagePolicy {string} - * @param skipUnavailableShards {boolean} - */ -const upgradeSingle = async (db, key, scripts, storagePolicy, skipUnavailableShards) => { - const _upgradeRequest = (request, useDefaultDB, updateVer) => { - return upgradeRequest({ - db, - useDefaultDB, - updateVer, - storage_policy: - storagePolicy, - skip_unavailable_shards: skipUnavailableShards - }, request) - } - if (!isDBCreated) { - isDBCreated = true - await _upgradeRequest('CREATE DATABASE IF NOT EXISTS {{DB}} {{{OnCluster}}}') - if (clusterName) { - await _upgradeRequest('CREATE TABLE IF NOT EXISTS {{DB}}._ver {{{OnCluster}}} (k UInt64, ver UInt64) ' + - 'ENGINE={{ReplacingMergeTree}}(ver) ORDER BY k {{{CREATE_SETTINGS}}}', true) - await _upgradeRequest('CREATE TABLE IF NOT EXISTS {{DB}}.ver {{{OnCluster}}} (k UInt64, ver UInt64) ' + - 'ENGINE=Distributed(\'{{CLUSTER}}\',\'{{DB}}\', \'_ver\', rand())', true) - } else { - await _upgradeRequest('CREATE TABLE IF NOT EXISTS {{DB}}.ver {{{OnCluster}}} (k UInt64, ver UInt64) ' + - 'ENGINE={{ReplacingMergeTree}}(ver) ORDER BY k {{{CREATE_SETTINGS}}}', true) - } - } - let ver = await client.rawRequest(`SELECT max(ver) as ver FROM ver WHERE k = ${key} FORMAT JSON`, - null, db) - ver = ver.data.data && ver.data.data[0] && ver.data.data[0].ver ? ver.data.data[0].ver : 0 - for (let i = parseInt(ver); i < scripts.length; ++i) { - if (!scripts[i]) { continue } - scripts[i] = scripts[i].trim() - await _upgradeRequest(scripts[i], true, { key: key, to: i + 1 }) - } -} - -/** - * @param opts {{ - * db: string, - * useDefaultDB: boolean, - * updateVer: {key: number, to: number}, - * storage_policy: string, - * skip_unavailable_shards: boolean - * }} - * @param request {string} database to update - * @returns {Promise<void>} - */ -const upgradeRequest = async (opts, request) => { - const tpl = hb.compile(request) - request = tpl({ - ...getEnv(), - DB: opts.db, - CLUSTER: clusterName || '', - SAMPLES_ORDER_RUL: samplesOrderingRule(), - OnCluster: clusterName ? `ON CLUSTER \`${clusterName}\`` : '', - MergeTree: clusterName ? 'ReplicatedMergeTree' : 'MergeTree', - ReplacingMergeTree: clusterName ? 'ReplicatedReplacingMergeTree' : 'ReplacingMergeTree', - AggregatingMergeTree: clusterName ? 'ReplicatedAggregatingMergeTree' : 'AggregatingMergeTree', - CREATE_SETTINGS: opts.storage_policy ? `SETTINGS storage_policy='${opts.storage_policy}'` : '', - DIST_CREATE_SETTINGS: opts.skip_unavailable_shards ? `SETTINGS skip_unavailable_shards=1` : '' - }) - await client.rawRequest(request, null, opts.useDefaultDB ? opts.db : undefined) - if (opts.updateVer) { - await client.rawRequest(`INSERT INTO ver (k, ver) VALUES (${opts.updateVer.key}, ${opts.updateVer.to})`, - null, opts.db) - } -} - -/** - * @param opts {{db: string, samples_days: number, time_series_days: number, storage_policy: string}[]} - * @returns {Promise<void>} - */ -module.exports.rotate = async (opts) => { - for (const db of opts) { - const settings = await client.getSettings([ - { type: 'rotate', name: 'v3_samples_days' }, - { type: 'rotate', name: 'v3_time_series_days' }, - { type: 'rotate', name: 'v3_storage_policy' }, - { type: 'rotate', name: 'v1_traces_days' }, - { type: 'rotate', name: 'v1_traces_storage_policy' }, - { type: 'rotate', name: 'v1_profiles_days' } - ], db.db) - const _update = (req) => { - return upgradeRequest({ db: db.db, useDefaultDB: true }, req) - } - if (db.samples_days + '' !== settings.v3_samples_days) { - const alterTable = 'ALTER TABLE {{DB}}.samples_v3 {{{OnCluster}}} MODIFY SETTING ttl_only_drop_parts = 1, merge_with_ttl_timeout = 3600, index_granularity = 8192' - const rotateTable = `ALTER TABLE {{DB}}.samples_v3 {{{OnCluster}}} MODIFY TTL toDateTime(timestamp_ns / 1000000000) + INTERVAL ${db.samples_days} DAY` - await _update(alterTable, null, db.db) - await _update(rotateTable, null, db.db) - await client.addSetting('rotate', 'v3_samples_days', db.samples_days + '', db.db) - } - if (db.time_series_days + '' !== settings.v3_time_series_days) { - const alterTable = 'ALTER TABLE {{DB}}.time_series {{{OnCluster}}} MODIFY SETTING ttl_only_drop_parts = 1, merge_with_ttl_timeout = 3600, index_granularity = 8192' - const rotateTable = `ALTER TABLE {{DB}}.time_series {{{OnCluster}}} MODIFY TTL "date" + INTERVAL ${db.time_series_days} DAY` - await _update(alterTable, null, db.db) - await _update(rotateTable, null, db.db) - const alterView = 'ALTER TABLE time_series_gin {{{OnCluster}}} MODIFY SETTING ttl_only_drop_parts = 1, merge_with_ttl_timeout = 3600, index_granularity = 8192' - const rotateView = `ALTER TABLE time_series_gin {{{OnCluster}}} MODIFY TTL "date" + INTERVAL ${db.time_series_days} DAY` - await _update(alterView, null, db.db) - await _update(rotateView, null, db.db) - await client.addSetting('rotate', 'v3_time_series_days', db.time_series_days + '', db.db) - } - if (db.storage_policy && db.storage_policy !== settings.v3_storage_policy) { - logger.debug(`Altering storage policy: ${db.storage_policy}`) - const alterTs = `ALTER TABLE {{DB}}.time_series {{{OnCluster}}} MODIFY SETTING storage_policy='${db.storage_policy}'` - const alterTsVw = `ALTER TABLE {{DB}}.time_series_gin {{{OnCluster}}} MODIFY SETTING storage_policy='${db.storage_policy}'` - const alterSm = `ALTER TABLE {{DB}}.samples_v3 {{{OnCluster}}} MODIFY SETTING storage_policy='${db.storage_policy}'` - await _update(alterTs, null, db.db) - await _update(alterTsVw, null, db.db) - await _update(alterSm, null, db.db) - await client.addSetting('rotate', 'v3_storage_policy', db.storage_policy, db.db) - } - if (db.samples_days + '' !== settings.v1_traces_days) { - let alterTable = 'ALTER TABLE {{DB}}.tempo_traces {{{OnCluster}}} MODIFY SETTING ttl_only_drop_parts = 1, merge_with_ttl_timeout = 3600, index_granularity = 8192' - let rotateTable = `ALTER TABLE {{DB}}.tempo_traces {{{OnCluster}}} MODIFY TTL toDateTime(timestamp_ns / 1000000000) + INTERVAL ${db.samples_days} DAY` - await _update(alterTable, null, db.db) - await _update(rotateTable, null, db.db) - alterTable = 'ALTER TABLE {{DB}}.tempo_traces_attrs_gin {{{OnCluster}}} MODIFY SETTING ttl_only_drop_parts = 1, merge_with_ttl_timeout = 3600, index_granularity = 8192' - rotateTable = `ALTER TABLE {{DB}}.tempo_traces_attrs_gin {{{OnCluster}}} MODIFY TTL date + INTERVAL ${db.samples_days} DAY` - await _update(alterTable, null, db.db) - await _update(rotateTable, null, db.db) - alterTable = 'ALTER TABLE {{DB}}.tempo_traces_kv {{{OnCluster}}} MODIFY SETTING ttl_only_drop_parts = 1, merge_with_ttl_timeout = 3600, index_granularity = 8192' - rotateTable = `ALTER TABLE {{DB}}.tempo_traces_kv {{{OnCluster}}} MODIFY TTL date + INTERVAL ${db.samples_days} DAY` - await _update(alterTable, null, db.db) - await _update(rotateTable, null, db.db) - await client.addSetting('rotate', 'v1_traces_days', db.samples_days + '', db.db) - } - if (db.storage_policy && db.storage_policy !== settings.v1_traces_storage_policy) { - logger.debug(`Altering storage policy: ${db.storage_policy}`) - const alterTs = `ALTER TABLE {{DB}}.tempo_traces MODIFY SETTING storage_policy='${db.storage_policy}'` - const alterTsVw = `ALTER TABLE {{DB}}.tempo_traces_attrs_gin MODIFY SETTING storage_policy='${db.storage_policy}'` - const alterSm = `ALTER TABLE {{DB}}.tempo_traces_kv MODIFY SETTING storage_policy='${db.storage_policy}'` - await _update(alterTs, null, db.db) - await _update(alterTsVw, null, db.db) - await _update(alterSm, null, db.db) - await client.addSetting('rotate', 'v1_traces_storage_policy', db.storage_policy, db.db) - } - if (db.samples_days + '' !== settings.v1_profiles_days) { - let alterTable = 'ALTER TABLE {{DB}}.profiles {{{OnCluster}}} MODIFY SETTING ttl_only_drop_parts = 1, merge_with_ttl_timeout = 3600, index_granularity = 8192' - let rotateTable = `ALTER TABLE {{DB}}.profiles {{{OnCluster}}} MODIFY TTL toDateTime(timestamp_ns / 1000000000) + INTERVAL ${db.samples_days} DAY` - await _update(alterTable, null, db.db) - await _update(rotateTable, null, db.db) - alterTable = 'ALTER TABLE {{DB}}.profiles_series {{{OnCluster}}} MODIFY SETTING ttl_only_drop_parts = 1, merge_with_ttl_timeout = 3600, index_granularity = 8192' - rotateTable = `ALTER TABLE {{DB}}.profiles_series {{{OnCluster}}} MODIFY TTL date + INTERVAL ${db.samples_days} DAY` - await _update(alterTable, null, db.db) - await _update(rotateTable, null, db.db) - alterTable = 'ALTER TABLE {{DB}}.profiles_series_gin {{{OnCluster}}} MODIFY SETTING ttl_only_drop_parts = 1, merge_with_ttl_timeout = 3600, index_granularity = 8192' - rotateTable = `ALTER TABLE {{DB}}.profiles_series_gin {{{OnCluster}}} MODIFY TTL date + INTERVAL ${db.samples_days} DAY` - await _update(alterTable, null, db.db) - await _update(rotateTable, null, db.db) - alterTable = 'ALTER TABLE {{DB}}.profiles_series_keys {{{OnCluster}}} MODIFY SETTING ttl_only_drop_parts = 1, merge_with_ttl_timeout = 3600, index_granularity = 8192' - rotateTable = `ALTER TABLE {{DB}}.profiles_series_keys {{{OnCluster}}} MODIFY TTL date + INTERVAL ${db.samples_days} DAY` - await _update(alterTable, null, db.db) - await _update(rotateTable, null, db.db) - await client.addSetting('rotate', 'v1_profiles_days', db.samples_days + '', db.db) - } - } -} diff --git a/lib/db/maintain/scripts.js b/lib/db/maintain/scripts.js deleted file mode 100644 index 1311c878..00000000 --- a/lib/db/maintain/scripts.js +++ /dev/null @@ -1,481 +0,0 @@ -module.exports.overall = [ - `CREATE TABLE IF NOT EXISTS {{DB}}.time_series {{{OnCluster}}} (date Date,fingerprint UInt64,labels String, name String) - ENGINE = {{ReplacingMergeTree}}(date) PARTITION BY date ORDER BY fingerprint {{{CREATE_SETTINGS}}}`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.samples_v3 {{{OnCluster}}} - ( - fingerprint UInt64, - timestamp_ns Int64 CODEC(DoubleDelta), - value Float64 CODEC(Gorilla), - string String - ) ENGINE = {{MergeTree}} - PARTITION BY toStartOfDay(toDateTime(timestamp_ns / 1000000000)) - ORDER BY ({{SAMPLES_ORDER_RUL}}) {{{CREATE_SETTINGS}}}`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.settings {{{OnCluster}}} - (fingerprint UInt64, type String, name String, value String, inserted_at DateTime64(9, 'UTC')) - ENGINE = {{ReplacingMergeTree}}(inserted_at) ORDER BY fingerprint {{{CREATE_SETTINGS}}}`, - - 'DROP TABLE IF EXISTS {{DB}}.samples_read {{{OnCluster}}}', - - `CREATE TABLE IF NOT EXISTS {{DB}}.samples_read {{{OnCluster}}} - (fingerprint UInt64,timestamp_ms Int64,value Float64,string String) - ENGINE=Merge('{{DB}}', '^(samples|samples_v2)$')`, - - `CREATE VIEW IF NOT EXISTS {{DB}}.samples_read_v2_1 {{{OnCluster}}} AS - SELECT fingerprint, timestamp_ms * 1000000 as timestamp_ns, value, string FROM samples_read`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.samples_read_v2_2 {{{OnCluster}}} - (fingerprint UInt64,timestamp_ns Int64,value Float64,string String) - ENGINE=Merge('{{DB}}', '^(samples_read_v2_1|samples_v3)$')`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.time_series_gin {{{OnCluster}}} ( - date Date, - key String, - val String, - fingerprint UInt64 - ) ENGINE = {{ReplacingMergeTree}}() PARTITION BY date ORDER BY (key, val, fingerprint) {{{CREATE_SETTINGS}}}`, - - `CREATE MATERIALIZED VIEW IF NOT EXISTS {{DB}}.time_series_gin_view {{{OnCluster}}} TO time_series_gin - AS SELECT date, pairs.1 as key, pairs.2 as val, fingerprint - FROM time_series ARRAY JOIN JSONExtractKeysAndValues(time_series.labels, 'String') as pairs`, - - `INSERT INTO {{DB}}.settings (fingerprint, type, name, value, inserted_at) VALUES (cityHash64('update_v3_5'), 'update', - 'v3_1', toString(toUnixTimestamp(NOW())), NOW())`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.metrics_15s {{{OnCluster}}} ( - fingerprint UInt64, - timestamp_ns Int64 CODEC(DoubleDelta), - last AggregateFunction(argMax, Float64, Int64), - max SimpleAggregateFunction(max, Float64), - min SimpleAggregateFunction(min, Float64), - count AggregateFunction(count), - sum SimpleAggregateFunction(sum, Float64), - bytes SimpleAggregateFunction(sum, Float64) -) ENGINE = {{AggregatingMergeTree}} -PARTITION BY toDate(toDateTime(intDiv(timestamp_ns, 1000000000))) -ORDER BY (fingerprint, timestamp_ns) {{{CREATE_SETTINGS}}};`, - - `CREATE MATERIALIZED VIEW IF NOT EXISTS {{DB}}.metrics_15s_mv {{{OnCluster}}} TO metrics_15s AS -SELECT fingerprint, - intDiv(samples.timestamp_ns, 15000000000) * 15000000000 as timestamp_ns, - argMaxState(value, samples.timestamp_ns) as last, - maxSimpleState(value) as max, - minSimpleState(value) as min, - countState() as count, - sumSimpleState(value) as sum, - sumSimpleState(length(string)) as bytes -FROM {{DB}}.samples_v3 as samples -GROUP BY fingerprint, timestamp_ns;`, - - `INSERT INTO {{DB}}.settings (fingerprint, type, name, value, inserted_at) VALUES (cityHash64('update_v3_2'), 'update', - 'v3_2', toString(toUnixTimestamp(NOW())), NOW())`, - "INSERT INTO {{DB}}.settings (fingerprint, type, name, value, inserted_at) VALUES (cityHash64('update_v3_2'), 'update', " + - "'v3_2', toString(toUnixTimestamp(NOW())), NOW())", - - `ALTER TABLE {{DB}}.time_series {{{OnCluster}}} - ADD COLUMN IF NOT EXISTS type UInt8, - MODIFY ORDER BY (fingerprint, type)`, - - `ALTER TABLE {{DB}}.samples_v3 {{{OnCluster}}} - ADD COLUMN IF NOT EXISTS type UInt8`, - - `ALTER TABLE {{DB}}.time_series_gin {{{OnCluster}}} - ADD COLUMN IF NOT EXISTS type UInt8, - MODIFY ORDER BY (key, val, fingerprint, type)`, - - `ALTER TABLE {{DB}}.metrics_15s {{{OnCluster}}} - ADD COLUMN IF NOT EXISTS type UInt8, - MODIFY ORDER BY (fingerprint, timestamp_ns, type)`, - - 'RENAME TABLE {{DB}}.time_series_gin_view TO time_series_gin_view_bak {{{OnCluster}}}', - - `CREATE MATERIALIZED VIEW IF NOT EXISTS {{DB}}.time_series_gin_view {{{OnCluster}}} TO time_series_gin - AS SELECT date, pairs.1 as key, pairs.2 as val, fingerprint, type - FROM time_series ARRAY JOIN JSONExtractKeysAndValues(time_series.labels, 'String') as pairs`, - - 'DROP TABLE IF EXISTS {{DB}}.time_series_gin_view_bak {{{OnCluster}}}', - - 'RENAME TABLE {{DB}}.metrics_15s_mv TO metrics_15s_mv_bak {{{OnCluster}}}', - - `CREATE MATERIALIZED VIEW IF NOT EXISTS {{DB}}.metrics_15s_mv {{{OnCluster}}} TO metrics_15s AS -SELECT fingerprint, - intDiv(samples.timestamp_ns, 15000000000) * 15000000000 as timestamp_ns, - argMaxState(value, samples.timestamp_ns) as last, - maxSimpleState(value) as max, - minSimpleState(value) as min, - countState() as count, - sumSimpleState(value) as sum, - sumSimpleState(length(string)) as bytes, - type -FROM samples_v3 as samples -GROUP BY fingerprint, timestamp_ns, type;`, - - 'DROP TABLE IF EXISTS {{DB}}.metrics_15s_mv_bak {{{OnCluster}}}' -] - -module.exports.traces = [ - `CREATE TABLE IF NOT EXISTS {{DB}}.tempo_traces {{{OnCluster}}} ( - oid String DEFAULT '0', - trace_id FixedString(16), - span_id FixedString(8), - parent_id String, - name String, - timestamp_ns Int64 CODEC(DoubleDelta), - duration_ns Int64, - service_name String, - payload_type Int8, - payload String - ) Engine = {{MergeTree}}() ORDER BY (oid, trace_id, timestamp_ns) - PARTITION BY (oid, toDate(FROM_UNIXTIME(intDiv(timestamp_ns, 1000000000)))) {{{CREATE_SETTINGS}}};`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.tempo_traces_attrs_gin {{{OnCluster}}} ( - oid String, - date Date, - key String, - val String, - trace_id FixedString(16), - span_id FixedString(8), - timestamp_ns Int64, - duration Int64 - ) Engine = {{ReplacingMergeTree}}() - PARTITION BY date - ORDER BY (oid, date, key, val, timestamp_ns, trace_id, span_id) {{{CREATE_SETTINGS}}};`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.tempo_traces_kv {{{OnCluster}}} ( - oid String, - date Date, - key String, - val_id UInt64, - val String - ) Engine = {{ReplacingMergeTree}}() - PARTITION BY (oid, date) - ORDER BY (oid, date, key, val_id) {{{CREATE_SETTINGS}}}`, - - `CREATE MATERIALIZED VIEW IF NOT EXISTS {{DB}}.tempo_traces_kv_mv {{{OnCluster}}} TO tempo_traces_kv AS - SELECT oid, date, key, cityHash64(val) % 10000 as val_id, val FROM tempo_traces_attrs_gin`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.traces_input {{{OnCluster}}} ( - oid String DEFAULT '0', - trace_id String, - span_id String, - parent_id String, - name String, - timestamp_ns Int64 CODEC(DoubleDelta), - duration_ns Int64, - service_name String, - payload_type Int8, - payload String, - tags Array(Tuple(String, String)) - ) Engine=Null`, - - `CREATE MATERIALIZED VIEW IF NOT EXISTS {{DB}}.traces_input_traces_mv {{{OnCluster}}} TO tempo_traces AS - SELECT oid, - unhex(trace_id)::FixedString(16) as trace_id, - unhex(span_id)::FixedString(8) as span_id, - unhex(parent_id) as parent_id, - name, - timestamp_ns, - duration_ns, - service_name, - payload_type, - payload - FROM traces_input`, - - `CREATE MATERIALIZED VIEW IF NOT EXISTS {{DB}}.traces_input_tags_mv {{{OnCluster}}} TO tempo_traces_attrs_gin AS - SELECT oid, - toDate(intDiv(timestamp_ns, 1000000000)) as date, - tags.1 as key, - tags.2 as val, - unhex(trace_id)::FixedString(16) as trace_id, - unhex(span_id)::FixedString(8) as span_id, - timestamp_ns, - duration_ns as duration - FROM traces_input ARRAY JOIN tags`, - - `INSERT INTO {{DB}}.settings (fingerprint, type, name, value, inserted_at) VALUES (cityHash64('tempo_traces_v1'), 'update', - 'tempo_traces_v2', toString(toUnixTimestamp(NOW())), NOW())` -] - -module.exports.overall_dist = [ - `CREATE TABLE IF NOT EXISTS {{DB}}.metrics_15s_dist {{{OnCluster}}} ( - \`fingerprint\` UInt64, - \`timestamp_ns\` Int64 CODEC(DoubleDelta), - \`last\` AggregateFunction(argMax, Float64, Int64), - \`max\` SimpleAggregateFunction(max, Float64), - \`min\` SimpleAggregateFunction(min, Float64), - \`count\` AggregateFunction(count), - \`sum\` SimpleAggregateFunction(sum, Float64), - \`bytes\` SimpleAggregateFunction(sum, Float64) -) ENGINE = Distributed('{{CLUSTER}}', '{{DB}}', 'metrics_15s', fingerprint) {{{DIST_CREATE_SETTINGS}}};`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.samples_v3_dist {{{OnCluster}}} ( - \`fingerprint\` UInt64, - \`timestamp_ns\` Int64 CODEC(DoubleDelta), - \`value\` Float64 CODEC(Gorilla), - \`string\` String -) ENGINE = Distributed('{{CLUSTER}}','{{DB}}', 'samples_v3', fingerprint)`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.time_series_dist {{{OnCluster}}} ( - \`date\` Date, - \`fingerprint\` UInt64, - \`labels\` String, - \`name\` String -) ENGINE = Distributed('{{CLUSTER}}','{{DB}}', 'time_series', fingerprint) {{{DIST_CREATE_SETTINGS}}};`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.settings_dist {{{OnCluster}}} ( - \`fingerprint\` UInt64, - \`type\` String, - \`name\` String, - \`value\` String, - \`inserted_at\` DateTime64(9, 'UTC') -) ENGINE = Distributed('{{CLUSTER}}','{{DB}}', 'settings', rand()) {{{DIST_CREATE_SETTINGS}}};`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.time_series_gin_dist {{{OnCluster}}} ( - date Date, - key String, - val String, - fingerprint UInt64 - ) ENGINE = Distributed('{{CLUSTER}}','{{DB}}', 'time_series_gin', rand()) {{{DIST_CREATE_SETTINGS}}};`, - - 'ALTER TABLE {{DB}}.metrics_15s_dist {{{OnCluster}}} ADD COLUMN IF NOT EXISTS `type` UInt8;', - - 'ALTER TABLE {{DB}}.samples_v3_dist {{{OnCluster}}} ADD COLUMN IF NOT EXISTS `type` UInt8', - - 'ALTER TABLE {{DB}}.time_series_dist {{{OnCluster}}} ADD COLUMN IF NOT EXISTS `type` UInt8;', - - 'ALTER TABLE {{DB}}.time_series_gin_dist {{{OnCluster}}} ADD COLUMN IF NOT EXISTS `type` UInt8;' -] - -module.exports.traces_dist = [ - `CREATE TABLE IF NOT EXISTS {{DB}}.tempo_traces_kv_dist {{{OnCluster}}} ( - oid String, - date Date, - key String, - val_id String, - val String -) ENGINE = Distributed('{{CLUSTER}}','{{DB}}', 'tempo_traces_kv', sipHash64(oid, key)) {{{DIST_CREATE_SETTINGS}}};`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.tempo_traces_dist {{{OnCluster}}} ( - oid String, - trace_id FixedString(16), - span_id FixedString(8), - parent_id String, - name String, - timestamp_ns Int64 CODEC(DoubleDelta), - duration_ns Int64, - service_name String, - payload_type Int8, - payload String, -) ENGINE = Distributed('{{CLUSTER}}','{{DB}}', 'tempo_traces', sipHash64(oid, trace_id)) {{{DIST_CREATE_SETTINGS}}};`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.tempo_traces_attrs_gin_dist {{{OnCluster}}} ( - oid String, - date Date, - key String, - val String, - trace_id FixedString(16), - span_id FixedString(8), - timestamp_ns Int64, - duration Int64 -) ENGINE = Distributed('{{CLUSTER}}','{{DB}}', 'tempo_traces_attrs_gin', sipHash64(oid, trace_id)) {{{DIST_CREATE_SETTINGS}}};` -] - -module.exports.profiles = [ - `CREATE TABLE IF NOT EXISTS {{DB}}.profiles_input {{{OnCluster}}} ( - timestamp_ns UInt64, - type LowCardinality(String), - service_name LowCardinality(String), - sample_types_units Array(Tuple(String, String)), - period_type LowCardinality(String), - period_unit LowCardinality(String), - tags Array(Tuple(String, String)), - duration_ns UInt64, - payload_type LowCardinality(String), - payload String, - values_agg Array(Tuple(String, Int64, Int32)) CODEC(ZSTD(1)) - ) Engine=Null`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.profiles {{{OnCluster}}} ( - timestamp_ns UInt64 CODEC(DoubleDelta, ZSTD(1)), - fingerprint UInt64 CODEC(DoubleDelta, ZSTD(1)), - type_id LowCardinality(String) CODEC(ZSTD(1)), - sample_types_units Array(Tuple(String, String)) CODEC(ZSTD(1)), - service_name LowCardinality(String) CODEC(ZSTD(1)), - duration_ns UInt64 CODEC(DoubleDelta, ZSTD(1)), - payload_type LowCardinality(String) CODEC(ZSTD(1)), - payload String CODEC(ZSTD(1)), - values_agg Array(Tuple(String, Int64, Int32)) CODEC(ZSTD(1)) - ) Engine {{MergeTree}}() - ORDER BY (type_id, service_name, timestamp_ns) - PARTITION BY toDate(FROM_UNIXTIME(intDiv(timestamp_ns, 1000000000))) {{{CREATE_SETTINGS}}}`, - - `CREATE MATERIALIZED VIEW IF NOT EXISTS {{DB}}.profiles_mv {{{OnCluster}}} TO profiles AS - SELECT - timestamp_ns, - cityHash64(arraySort(arrayConcat( - profiles_input.tags, [ - ('__type__', concatWithSeparator(':', type, period_type, period_unit) as _type_id), - ('__sample_types_units__', arrayStringConcat(arrayMap(x -> x.1 || ':' || x.2, arraySort(sample_types_units)), ';')), - ('service_name', service_name) - ])) as _tags) as fingerprint, - _type_id as type_id, - sample_types_units, - service_name, - duration_ns, - payload_type, - payload, - values_agg - FROM profiles_input`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.profiles_series {{{OnCluster}}} ( - date Date CODEC(ZSTD(1)), - type_id LowCardinality(String) CODEC(ZSTD(1)), - sample_types_units Array(Tuple(String, String)) CODEC(ZSTD(1)), - service_name LowCardinality(String) CODEC(ZSTD(1)), - fingerprint UInt64 CODEC(DoubleDelta, ZSTD(1)), - tags Array(Tuple(String, String)) CODEC(ZSTD(1)), - ) Engine {{ReplacingMergeTree}}() - ORDER BY (date, type_id, fingerprint) - PARTITION BY date {{{CREATE_SETTINGS}}}`, - - `CREATE MATERIALIZED VIEW IF NOT EXISTS {{DB}}.profiles_series_mv {{{OnCluster}}} TO profiles_series AS - SELECT - toDate(intDiv(timestamp_ns, 1000000000)) as date, - concatWithSeparator(':', type, period_type, period_unit) as type_id, - sample_types_units, - service_name, - cityHash64(arraySort(arrayConcat( - profiles_input.tags, [ - ('__type__', type_id), - ('__sample_types_units__', arrayStringConcat(arrayMap(x -> x.1 || ':' || x.2, arraySort(sample_types_units)), ';')), - ('service_name', service_name) - ])) as _tags) as fingerprint, - arrayConcat(profiles_input.tags, [('service_name', service_name)]) as tags - FROM profiles_input`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.profiles_series_gin {{{OnCluster}}} ( - date Date CODEC(ZSTD(1)), - key String CODEC(ZSTD(1)), - val String CODEC(ZSTD(1)), - type_id LowCardinality(String) CODEC(ZSTD(1)), - sample_types_units Array(Tuple(String, String)), - service_name LowCardinality(String) CODEC(ZSTD(1)), - fingerprint UInt64 CODEC(DoubleDelta, ZSTD(1)), - ) Engine {{ReplacingMergeTree}}() - ORDER BY (date, key, val, type_id, fingerprint) - PARTITION BY date {{{CREATE_SETTINGS}}}`, - - `CREATE MATERIALIZED VIEW IF NOT EXISTS {{DB}}.profiles_series_gin_mv {{{OnCluster}}} TO profiles_series_gin AS - SELECT - date, - kv.1 as key, - kv.2 as val, - type_id, - sample_types_units, - service_name, - fingerprint - FROM profiles_series ARRAY JOIN tags as kv`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.profiles_series_keys {{{OnCluster}}} ( - date Date, - key String, - val String, - val_id UInt64 - ) Engine {{ReplacingMergeTree}}() - ORDER BY (date, key, val_id) - PARTITION BY date {{{CREATE_SETTINGS}}}`, - - `CREATE MATERIALIZED VIEW IF NOT EXISTS {{DB}}.profiles_series_keys_mv {{{OnCluster}}} TO profiles_series_keys AS - SELECT - date, - key, - val, - cityHash64(val) % 50000 as val_id - FROM profiles_series_gin`, - - `ALTER TABLE {{DB}}.profiles_input {{{OnCluster}}} - ADD COLUMN IF NOT EXISTS \`tree\` Array(Tuple(UInt64, UInt64, UInt64, Array(Tuple(String, Int64, Int64)))), - ADD COLUMN IF NOT EXISTS \`functions\` Array(Tuple(UInt64, String))`, - - `ALTER TABLE {{DB}}.profiles {{{OnCluster}}} - ADD COLUMN IF NOT EXISTS \`tree\` Array(Tuple(UInt64, UInt64, UInt64, Array(Tuple(String, Int64, Int64)))), - ADD COLUMN IF NOT EXISTS \`functions\` Array(Tuple(UInt64, String))`, - - 'RENAME TABLE IF EXISTS {{DB}}.profiles_mv TO profiles_mv_bak {{{OnCluster}}}', - - `CREATE MATERIALIZED VIEW IF NOT EXISTS {{DB}}.profiles_mv {{{OnCluster}}} TO profiles AS - SELECT - timestamp_ns, - cityHash64(arraySort(arrayConcat( - profiles_input.tags, [ - ('__type__', concatWithSeparator(':', type, period_type, period_unit) as _type_id), - ('__sample_types_units__', arrayStringConcat(arrayMap(x -> x.1 || ':' || x.2, arraySort(sample_types_units)), ';')), - ('service_name', service_name) - ])) as _tags) as fingerprint, - _type_id as type_id, - sample_types_units, - service_name, - duration_ns, - payload_type, - payload, - values_agg, - tree, - functions - FROM profiles_input`, - - 'DROP TABLE IF EXISTS {{DB}}.profiles_mv_bak {{{OnCluster}}}', - - "INSERT INTO {{DB}}.settings (fingerprint, type, name, value, inserted_at) VALUES (cityHash64('profiles_v2'), 'update', " + - "'profiles_v2', toString(toUnixTimestamp(NOW())), NOW())" -] - -module.exports.profiles_dist = [ - `CREATE TABLE IF NOT EXISTS {{DB}}.profiles_dist {{{OnCluster}}} ( - timestamp_ns UInt64, - fingerprint UInt64, - type_id LowCardinality(String), - service_name LowCardinality(String), - duration_ns UInt64, - payload_type LowCardinality(String), - payload String, - values_agg Array(Tuple(String, Int64, Int32)) - ) ENGINE = Distributed('{{CLUSTER}}','{{DB}}','profiles', fingerprint) {{{DIST_CREATE_SETTINGS}}};`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.profiles_series_dist {{{OnCluster}}} ( - date Date, - type_id LowCardinality(String), - service_name LowCardinality(String), - fingerprint UInt64 CODEC(DoubleDelta, ZSTD(1)), - tags Array(Tuple(String, String)) CODEC(ZSTD(1)) - ) ENGINE = Distributed('{{CLUSTER}}','{{DB}}','profiles_series',fingerprint) {{{DIST_CREATE_SETTINGS}}};`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.profiles_series_gin_dist {{{OnCluster}}} ( - date Date, - key String, - val String, - type_id LowCardinality(String), - service_name LowCardinality(String), - fingerprint UInt64 CODEC(DoubleDelta, ZSTD(1)) - ) ENGINE = Distributed('{{CLUSTER}}','{{DB}}','profiles_series_gin',fingerprint) {{{DIST_CREATE_SETTINGS}}};`, - - `CREATE TABLE IF NOT EXISTS {{DB}}.profiles_series_keys_dist {{{OnCluster}}} ( - date Date, - key String, - val String, - val_id UInt64 - ) ENGINE = Distributed('{{CLUSTER}}','{{DB}}','profiles_series_keys', rand()) {{{DIST_CREATE_SETTINGS}}};`, - - `ALTER TABLE {{DB}}.profiles_dist {{{OnCluster}}} - ADD COLUMN IF NOT EXISTS \`tree\` Array(Tuple(UInt64, UInt64, UInt64, Array(Tuple(String, Int64, Int64)))), - ADD COLUMN IF NOT EXISTS \`functions\` Array(Tuple(UInt64, String))`, - - `ALTER TABLE {{DB}}.profiles_dist {{{OnCluster}}} - ADD COLUMN IF NOT EXISTS \`sample_types_units\` Array(Tuple(String, String))`, - - `ALTER TABLE {{DB}}.profiles_series_dist {{{OnCluster}}} - ADD COLUMN IF NOT EXISTS \`sample_types_units\` Array(Tuple(String, String))`, - - `ALTER TABLE {{DB}}.profiles_series_gin_dist {{{OnCluster}}} - ADD COLUMN IF NOT EXISTS \`sample_types_units\` Array(Tuple(String, String))` -] diff --git a/lib/db/otlp.js b/lib/db/otlp.js deleted file mode 100644 index fa1269c2..00000000 --- a/lib/db/otlp.js +++ /dev/null @@ -1,81 +0,0 @@ -const crypto = require('crypto') -const { codeToString, flatOTLPAttrs, OTLPgetServiceNames } = require('../utils') -module.exports = class { - constructor (obj) { - if (obj.parentId) { - obj.parentId = this.getId(obj.parentSpanId, 16, false) - } - const { local: serviceName, remote: remoteServiceName } = OTLPgetServiceNames(flatOTLPAttrs(obj.attributes)) - - this.span_id = this.getId(obj.spanId, 16, true) - this.trace_id = this.getId(obj.traceId, 32, true) - this.parent_id = this.getId(obj.parentSpanId, 16, false) - this.name = obj.name || '' - this.timestamp_ns = BigInt(obj.startTimeUnixNano) - this.duration_ns = BigInt(obj.endTimeUnixNano) - this.timestamp_ns - this.service_name = serviceName - this.payload_type = 2 - this.payload = JSON.stringify(obj) - this.tags = {} - this.tags.name = this.name - - if (obj.status) { - this.tags['status.code'] = 'code' in obj.status - ? codeToString(obj.status.code) - : 'Ok' - if ('message' in obj.status) { - this.tags['status.description'] = obj.status.message - } - } - - for (const tag of obj.attributes || []) { - let val = '' - if (!tag.value) { - continue - } - val = ((tag) => { - for (const valueKey of ['stringValue', 'boolValue', 'intValue', 'doubleValue']) { - if (typeof tag.value[valueKey] !== 'undefined') { - return `${tag.value[valueKey]}` - } - } - return undefined - })(tag) - val = val || JSON.stringify(tag.value) - this.tags[tag.key] = val - } - this.tags['service.name'] = serviceName - this.tags['remoteService.name'] = remoteServiceName - - this.tags = Object.entries(this.tags) - } - - /** - * @returns {string} - */ - toJson () { - return JSON.stringify(this, (k, val) => typeof val === 'bigint' ? val.toString() : val) - } - - /** - * - * @param strId {string} - * @param size {number} - * @param defaultRandom {boolean} - * @returns {string} - */ - getId (strId, size, defaultRandom) { - if (!strId) { - return undefined - } - strId = Buffer.from(strId, 'base64').toString('hex') - strId = (new Array(size)).fill('0').join('') + strId - strId = strId.substring(strId.length - size) - if (strId && strId.match(new RegExp(`^[0-9a-f]{${size}}$`))) { - return strId - } - return defaultRandom - ? crypto.randomUUID().toString().replace(/-/g, '').substring(0, size) - : null - } -} diff --git a/lib/db/throttler.js b/lib/db/throttler.js deleted file mode 100644 index 90fc492c..00000000 --- a/lib/db/throttler.js +++ /dev/null @@ -1,194 +0,0 @@ -const { isMainThread, parentPort } = require('worker_threads') -const clickhouseOptions = require('./clickhouse_options').databaseOptions -const logger = require('../logger') -const { DATABASE_NAME } = require('../utils') -const clusterName = require('../../common').clusterName -const dist = clusterName ? '_dist' : '' -const { EventEmitter } = require('events') - -// variables to be initialized in the init() function due to the './clickhouse.js' cross-dependency & bun -let samplesThrottler -let timeSeriesThrottler -let tracesThottler -let samplesTableName -let rawRequest - -const axiosError = async (err) => { - console.log('axiosError', err) - try { - const resp = err.response - if (resp) { - if (typeof resp.data === 'object') { - err.responseData = '' - err.response.data.on('data', data => { err.responseData += data }) - await new Promise((resolve) => err.response.data.once('end', resolve)) - } - if (typeof resp.data === 'string') { - err.responseData = resp.data - } - return new Error('AXIOS ERROR: ' + err + - (err.responseData ? ' Response data: ' + err.responseData : '')) - } - } catch (e) { - console.log(e) - return err - } -} - -class TimeoutOrSizeThrottler { - constructor (statement, maxSizeB, maxAgeMS) { - this.statement = statement - this.queue = [] - this.resolvers = [] - this.rejects = [] - this.size = 0 - - this.maxSizeB = maxSizeB - this.maxAgeMs = maxAgeMS - this.lastRequest = 0 - } - - /** - * @param message {string} - */ - queuePush (message) { - this.queue.push(message) - this.size += message.length - } - - willFlush () { - return (this.maxSizeB && this.size > this.maxSizeB) || - (this.maxAgeMs && Date.now() - this.lastRequest > this.maxAgeMs) - } - - /** - * @param force {boolean} - * @returns {Promise<void>} - */ - async flush (force) { - try { - if (!force && !this.willFlush()) { - return - } - this.lastRequest = Date.now() - await this._flush() - this.resolvers.forEach(r => r()) - } catch (err) { - logger.error(await axiosError(err), 'AXIOS ERROR') - this.rejects.forEach(r => r(err)) - } - this.resolvers = [] - this.rejects = [] - } - - async _flush () { - const len = this.queue.length - if (len < 1) { - return - } - const _queue = this.queue - this.queue = [] - this.size = 0 - await rawRequest(this.statement, _queue.join('\n'), DATABASE_NAME(), { maxBodyLength: Infinity }) - } - - stop () { - this.on = false - } -} - -const emitter = new EventEmitter() -let on = true -const postMessage = message => { - const genericRequest = (throttler) => { - throttler.queuePush(message.data) - throttler.resolvers.push(() => { - if (isMainThread) { - emitter.emit('message', { status: 'ok', id: message.id }) - return - } - parentPort.postMessage({ status: 'ok', id: message.id }) - }) - throttler.rejects.push(() => { - if (isMainThread) { - emitter.emit('message', { status: 'err', id: message.id }) - return - } - parentPort.postMessage({ status: 'err', id: message.id }) - }) - } - switch (message.type) { - case 'end': - on = false - if (!isMainThread) { - parentPort.removeAllListeners('message') - } - break - case 'values': - genericRequest(samplesThrottler) - break - case 'labels': - genericRequest(timeSeriesThrottler) - break - case 'traces': - genericRequest(tracesThottler) - } -} - -const init = () => { - [samplesTableName, rawRequest] = [ - require('./clickhouse').samplesTableName, - require('./clickhouse').rawRequest - ] - - samplesThrottler = new TimeoutOrSizeThrottler( - `INSERT INTO ${clickhouseOptions.queryOptions.database}.${samplesTableName}${dist}(fingerprint, timestamp_ns, value, string, type) FORMAT JSONEachRow`, - parseInt(process.env.BULK_MAX_SIZE_BYTES || 0), parseInt(process.env.BULK_MAX_AGE_MS || 100)) - timeSeriesThrottler = new TimeoutOrSizeThrottler( - `INSERT INTO ${clickhouseOptions.queryOptions.database}.time_series${dist}(date, fingerprint, labels, name, type) FORMAT JSONEachRow`, - parseInt(process.env.BULK_MAX_SIZE_BYTES || 0), parseInt(process.env.BULK_MAX_AGE_MS || 100)) - tracesThottler = new TimeoutOrSizeThrottler( - `INSERT INTO ${clickhouseOptions.queryOptions.database}.traces_input - (trace_id, span_id, parent_id, name, timestamp_ns, duration_ns, service_name, payload_type, payload, tags) - FORMAT JSONEachRow`, - parseInt(process.env.BULK_MAX_SIZE_BYTES || 0), parseInt(process.env.BULK_MAX_AGE_MS || 100)) - - setTimeout(async () => { - // eslint-disable-next-line no-unmodified-loop-condition - while (on) { - try { - await Promise.all([ - (async () => { - await timeSeriesThrottler.flush(samplesThrottler.willFlush()) - await samplesThrottler.flush(false) - })(), - tracesThottler.flush(false) - ]) - } catch (err) { - logger.error(await axiosError(err), 'AXIOS ERROR') - } - await new Promise((resolve) => setTimeout(resolve, 100)) - } - }, 0) -} - -if (isMainThread) { - module.exports = { - samplesThrottler, - timeSeriesThrottler, - tracesThottler, - TimeoutThrottler: TimeoutOrSizeThrottler, - postMessage, - on: emitter.on.bind(emitter), - removeAllListeners: emitter.removeAllListeners.bind(emitter), - init, - terminate: () => { - postMessage({ type: 'end' }) - } - } -} else { - init() - parentPort.on('message', message => { - postMessage(message) - }) -} diff --git a/lib/db/watcher.js b/lib/db/watcher.js deleted file mode 100644 index 9dd89400..00000000 --- a/lib/db/watcher.js +++ /dev/null @@ -1,167 +0,0 @@ -const transpiler = require('../../parser/transpiler') -const crypto = require('crypto') -const EventEmitter = require('events') -const UTILS = require('../utils') -const { queryFingerprintsScan, createLiveView, watchLiveView } = require('./clickhouse') -const { capabilities, samplesTableName } = require('./clickhouse') -const logger = require('../logger') -const compiler = require('../../parser/bnf') -const { asyncLogError } = require('../../common') -/** - * - * @type {Object<string, { w: Watcher, c: number }>} - */ -const watchers = {} - -class Watcher extends EventEmitter { - /** - * - * @param request {string} - * @return {Watcher} - */ - static getWatcher (request) { - const script = compiler.ParseScript(request.query.trim()) - const strScript = script.rootToken.dropAll('OWSP').value - if (!watchers[strScript]) { - watchers[strScript] = { w: new Watcher(request, strScript), c: 1 } - } else { - watchers[strScript].c++ - } - return watchers[strScript].w - } - - constructor (request, id) { - super() - this.id = id - this.request = request - - this.step = UTILS.parseOrDefault(request.step, 5) * 1000 - const self = this - this.working = true - this.uid = crypto.randomUUID().toString().replace(/-/g, '') - this.initQuery().catch(e => { - if (self.working) { - self.emit('error', e.message + '\n' + e.stack) - } - }) - } - - initQuery () { - return capabilities.liveView ? this.initQueryWatchPoll() : this.initQueryCBPoll() - } - - async initQueryWatchPoll () { - try { - this.watch = true - const request = transpiler.transpileTail({ ...this.request, samplesTable: samplesTableName }) - const name = `watcher_${this.uid.toString().substr(2)}` - await createLiveView(name, request.query, { timeout_sec: 10 }) - this.flushInterval = setInterval(this.flush.bind(this), 1000) - while (this.working) { - const [promise, cancel] = await watchLiveView(name, undefined, - { res: this }, { stream: request.stream }) - this.cancel = cancel - await promise - } - } catch (err) { - asyncLogError(err, logger) - throw err - } - } - - async initQueryCBPoll () { - this.from = (Date.now() - 300000) - while (this.working) { - this.to = (Date.now() - 1000) - this.query = transpiler.transpile({ - ...this.request, - start: this.from * 1000000, - end: this.to * 1000000 - }) - this.query.step = this.step - const resp = await queryFingerprintsScan(this.query) - const self = this - resp.on('data', (d) => { self.write(d) }) - await new Promise((resolve, reject) => { - resp.once('end', resolve) - resp.once('finish', resolve) - resp.once('close', resolve) - resp.once('error', reject) - }) - this.resp = JSON.parse(this.resp).data.result - for (const stream of this.resp) { - for (const v of stream.values) { - this.from = Math.max(this.from, (parseFloat(v[0]) + 1000000) / 1000000) - } - } - this.flush() - this.resp = '' - await new Promise((resolve) => setTimeout(resolve, 1000)) - } - } - - writeHead () {} - isNewString (entry) { - return !this.last || entry.timestamp_ns > this.last[0].timestamp_ns || - (entry.timestamp_ns === this.last[0].timestamp_ns && - !this.last.some(e => e.fingerprint === entry.fingerprint && e.string === entry.string)) - } - - write (str) { - if (this.watch) { - this.resp = this.resp || {} - if (!this.isNewString(str)) { - return - } - this.last = !this.last || str.timestamp_ns !== this.last[0].timestamp_ns ? [] : this.last - this.last.push(str) - const hash = JSON.stringify(str.labels) - this.resp[hash] = this.resp[hash] || { stream: str.labels, values: [] } - this.resp[hash].values.push([`${str.timestamp_ns}`, str.string]) - return - } - this.resp = this.resp || '' - this.resp += str - } - - flush () { - if (!this.resp || Object.values(this.resp).length === 0) { - return - } - this.emit('data', JSON.stringify({ - streams: Object.values(this.resp) - })) - this.resp = {} - } - - end () { - if (this.watch) { - this.flush() - return - } - this.emit('data', JSON.stringify( - { - streams: JSON.parse(this.resp).data.result - } - )) - this.resp = '' - } - - destroy () { - watchers[this.id].c-- - if (watchers[this.id].c) { - return - } - this.working = false - this.removeAllListeners('data') - if (this.flushInterval) { - clearInterval(this.flushInterval) - } - if (this.cancel) { - this.cancel.cancel() - } - delete watchers[this.id] - } -} - -module.exports = Watcher diff --git a/lib/db/zipkin.js b/lib/db/zipkin.js deleted file mode 100644 index 3920dcfc..00000000 --- a/lib/db/zipkin.js +++ /dev/null @@ -1,55 +0,0 @@ -const crypto = require('crypto') -module.exports = class { - constructor (obj) { - if (obj.parentId) { - obj.parentId = this.getId(obj.parentId, 16, false) - } - this.span_id = this.getId(obj.id, 16, true) - this.trace_id = this.getId(obj.traceId, 32, true) - this.parent_id = this.getId(obj.parentId, 16, false) - this.name = obj.name || '' - this.timestamp_ns = BigInt(obj.timestamp) * BigInt(1000) - this.duration_ns = BigInt(obj.duration || 1) * BigInt(1000) - this.service_name = obj.localEndpoint?.serviceName || obj.remoteEndpoint?.serviceName || '' - this.payload_type = 1 - this.payload = JSON.stringify(obj) - this.tags = {} - this.tags.name = this.name - this.tags['service.name'] = this.service_name - for (const tag of Object.entries(obj.tags || {})) { - this.tags[tag[0]] = tag[1] - } - this.tags = Object.entries(this.tags) - } - - /** - * @returns {string} - */ - toJson () { - const res = { - ...this, - timestamp_ns: this.timestamp_ns.toString(), - duration_ns: this.duration_ns.toString() - } - return JSON.stringify(res) - //return JSON.stringify(this, (k, val) => typeof val === 'bigint' ? val.toString() : val) - } - - /** - * - * @param strId {string} - * @param size {number} - * @param defaultRandom {boolean} - * @returns {string} - */ - getId (strId, size, defaultRandom) { - strId = (new Array(size)).fill('0').join('') + strId - strId = strId.substring(strId.length - size) - if (strId && strId.match(new RegExp(`^[0-9a-f]{${size}}$`))) { - return strId - } - return defaultRandom - ? crypto.randomUUID().toString().replace(/-/g, '').substring(0, size) - : null - } -} diff --git a/lib/descriptor.proto b/lib/descriptor.proto deleted file mode 100644 index 19dec3dd..00000000 --- a/lib/descriptor.proto +++ /dev/null @@ -1,844 +0,0 @@ -syntax = "proto2"; - -package google.protobuf; -option go_package = "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor"; -option java_package = "com.google.protobuf"; -option java_outer_classname = "DescriptorProtos"; -option csharp_namespace = "Google.Protobuf.Reflection"; -option objc_class_prefix = "GPB"; -option cc_enable_arenas = true; - -// descriptor.proto must be optimized for speed because reflection-based -// algorithms don't work during bootstrapping. -option optimize_for = SPEED; - -// The protocol compiler can output a FileDescriptorSet containing the .proto -// files it parses. -message FileDescriptorSet { - repeated FileDescriptorProto file = 1; -} - -// Describes a complete .proto file. -message FileDescriptorProto { - optional string name = 1; // file name, relative to root of source tree - optional string package = 2; // e.g. "foo", "foo.bar", etc. - - // Names of files imported by this file. - repeated string dependency = 3; - // Indexes of the public imported files in the dependency list above. - repeated int32 public_dependency = 10; - // Indexes of the weak imported files in the dependency list. - // For Google-internal migration only. Do not use. - repeated int32 weak_dependency = 11; - - // All top-level definitions in this file. - repeated DescriptorProto message_type = 4; - repeated EnumDescriptorProto enum_type = 5; - repeated ServiceDescriptorProto service = 6; - repeated FieldDescriptorProto extension = 7; - - optional FileOptions options = 8; - - // This field contains optional information about the original source code. - // You may safely remove this entire field without harming runtime - // functionality of the descriptors -- the information is needed only by - // development tools. - optional SourceCodeInfo source_code_info = 9; - - // The syntax of the proto file. - // The supported values are "proto2" and "proto3". - optional string syntax = 12; -} - -// Describes a message type. -message DescriptorProto { - optional string name = 1; - - repeated FieldDescriptorProto field = 2; - repeated FieldDescriptorProto extension = 6; - - repeated DescriptorProto nested_type = 3; - repeated EnumDescriptorProto enum_type = 4; - - message ExtensionRange { - optional int32 start = 1; - optional int32 end = 2; - - optional ExtensionRangeOptions options = 3; - } - repeated ExtensionRange extension_range = 5; - - repeated OneofDescriptorProto oneof_decl = 8; - - optional MessageOptions options = 7; - - // Range of reserved tag numbers. Reserved tag numbers may not be used by - // fields or extension ranges in the same message. Reserved ranges may - // not overlap. - message ReservedRange { - optional int32 start = 1; // Inclusive. - optional int32 end = 2; // Exclusive. - } - repeated ReservedRange reserved_range = 9; - // Reserved field names, which may not be used by fields in the same message. - // A given name may only be reserved once. - repeated string reserved_name = 10; -} - -message ExtensionRangeOptions { - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -// Describes a field within a message. -message FieldDescriptorProto { - enum Type { - // 0 is reserved for errors. - // Order is weird for historical reasons. - TYPE_DOUBLE = 1; - TYPE_FLOAT = 2; - // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if - // negative values are likely. - TYPE_INT64 = 3; - TYPE_UINT64 = 4; - // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if - // negative values are likely. - TYPE_INT32 = 5; - TYPE_FIXED64 = 6; - TYPE_FIXED32 = 7; - TYPE_BOOL = 8; - TYPE_STRING = 9; - // Tag-delimited aggregate. - // Group type is deprecated and not supported in proto3. However, Proto3 - // implementations should still be able to parse the group wire format and - // treat group fields as unknown fields. - TYPE_GROUP = 10; - TYPE_MESSAGE = 11; // Length-delimited aggregate. - - // New in version 2. - TYPE_BYTES = 12; - TYPE_UINT32 = 13; - TYPE_ENUM = 14; - TYPE_SFIXED32 = 15; - TYPE_SFIXED64 = 16; - TYPE_SINT32 = 17; // Uses ZigZag encoding. - TYPE_SINT64 = 18; // Uses ZigZag encoding. - }; - - enum Label { - // 0 is reserved for errors - LABEL_OPTIONAL = 1; - LABEL_REQUIRED = 2; - LABEL_REPEATED = 3; - }; - - optional string name = 1; - optional int32 number = 3; - optional Label label = 4; - - // If type_name is set, this need not be set. If both this and type_name - // are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP. - optional Type type = 5; - - // For message and enum types, this is the name of the type. If the name - // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping - // rules are used to find the type (i.e. first the nested types within this - // message are searched, then within the parent, on up to the root - // namespace). - optional string type_name = 6; - - // For extensions, this is the name of the type being extended. It is - // resolved in the same manner as type_name. - optional string extendee = 2; - - // For numeric types, contains the original text representation of the value. - // For booleans, "true" or "false". - // For strings, contains the default text contents (not escaped in any way). - // For bytes, contains the C escaped value. All bytes >= 128 are escaped. - // TODO(kenton): Base-64 encode? - optional string default_value = 7; - - // If set, gives the index of a oneof in the containing type's oneof_decl - // list. This field is a member of that oneof. - optional int32 oneof_index = 9; - - // JSON name of this field. The value is set by protocol compiler. If the - // user has set a "json_name" option on this field, that option's value - // will be used. Otherwise, it's deduced from the field's name by converting - // it to camelCase. - optional string json_name = 10; - - optional FieldOptions options = 8; -} - -// Describes a oneof. -message OneofDescriptorProto { - optional string name = 1; - optional OneofOptions options = 2; -} - -// Describes an enum type. -message EnumDescriptorProto { - optional string name = 1; - - repeated EnumValueDescriptorProto value = 2; - - optional EnumOptions options = 3; - - // Range of reserved numeric values. Reserved values may not be used by - // entries in the same enum. Reserved ranges may not overlap. - // - // Note that this is distinct from DescriptorProto.ReservedRange in that it - // is inclusive such that it can appropriately represent the entire int32 - // domain. - message EnumReservedRange { - optional int32 start = 1; // Inclusive. - optional int32 end = 2; // Inclusive. - } - - // Range of reserved numeric values. Reserved numeric values may not be used - // by enum values in the same enum declaration. Reserved ranges may not - // overlap. - repeated EnumReservedRange reserved_range = 4; - - // Reserved enum value names, which may not be reused. A given name may only - // be reserved once. - repeated string reserved_name = 5; -} - -// Describes a value within an enum. -message EnumValueDescriptorProto { - optional string name = 1; - optional int32 number = 2; - - optional EnumValueOptions options = 3; -} - -// Describes a service. -message ServiceDescriptorProto { - optional string name = 1; - repeated MethodDescriptorProto method = 2; - - optional ServiceOptions options = 3; -} - -// Describes a method of a service. -message MethodDescriptorProto { - optional string name = 1; - - // Input and output type names. These are resolved in the same way as - // FieldDescriptorProto.type_name, but must refer to a message type. - optional string input_type = 2; - optional string output_type = 3; - - optional MethodOptions options = 4; - - // Identifies if client streams multiple client messages - optional bool client_streaming = 5 [default=false]; - // Identifies if server streams multiple server messages - optional bool server_streaming = 6 [default=false]; -} - - -// =================================================================== -// Options - -// Each of the definitions above may have "options" attached. These are -// just annotations which may cause code to be generated slightly differently -// or may contain hints for code that manipulates protocol messages. -// -// Clients may define custom options as extensions of the *Options messages. -// These extensions may not yet be known at parsing time, so the parser cannot -// store the values in them. Instead it stores them in a field in the *Options -// message called uninterpreted_option. This field must have the same name -// across all *Options messages. We then use this field to populate the -// extensions when we build a descriptor, at which point all protos have been -// parsed and so all extensions are known. -// -// Extension numbers for custom options may be chosen as follows: -// * For options which will only be used within a single application or -// organization, or for experimental options, use field numbers 50000 -// through 99999. It is up to you to ensure that you do not use the -// same number for multiple options. -// * For options which will be published and used publicly by multiple -// independent entities, e-mail protobuf-global-extension-registry@google.com -// to reserve extension numbers. Simply provide your project name (e.g. -// Objective-C plugin) and your project website (if available) -- there's no -// need to explain how you intend to use them. Usually you only need one -// extension number. You can declare multiple options with only one extension -// number by putting them in a sub-message. See the Custom Options section of -// the docs for examples: -// https://developers.google.com/protocol-buffers/docs/proto#options -// If this turns out to be popular, a web service will be set up -// to automatically assign option numbers. - - -message FileOptions { - - // Sets the Java package where classes generated from this .proto will be - // placed. By default, the proto package is used, but this is often - // inappropriate because proto packages do not normally start with backwards - // domain names. - optional string java_package = 1; - - - // If set, all the classes from the .proto file are wrapped in a single - // outer class with the given name. This applies to both Proto1 - // (equivalent to the old "--one_java_file" option) and Proto2 (where - // a .proto always translates to a single class, but you may want to - // explicitly choose the class name). - optional string java_outer_classname = 8; - - // If set true, then the Java code generator will generate a separate .java - // file for each top-level message, enum, and service defined in the .proto - // file. Thus, these types will *not* be nested inside the outer class - // named by java_outer_classname. However, the outer class will still be - // generated to contain the file's getDescriptor() method as well as any - // top-level extensions defined in the file. - optional bool java_multiple_files = 10 [default=false]; - - // This option does nothing. - optional bool java_generate_equals_and_hash = 20 [deprecated=true]; - - // If set true, then the Java2 code generator will generate code that - // throws an exception whenever an attempt is made to assign a non-UTF-8 - // byte sequence to a string field. - // Message reflection will do the same. - // However, an extension field still accepts non-UTF-8 byte sequences. - // This option has no effect on when used with the lite runtime. - optional bool java_string_check_utf8 = 27 [default=false]; - - - // Generated classes can be optimized for speed or code size. - enum OptimizeMode { - SPEED = 1; // Generate complete code for parsing, serialization, - // etc. - CODE_SIZE = 2; // Use ReflectionOps to implement these methods. - LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime. - } - optional OptimizeMode optimize_for = 9 [default=SPEED]; - - // Sets the Go package where structs generated from this .proto will be - // placed. If omitted, the Go package will be derived from the following: - // - The basename of the package import path, if provided. - // - Otherwise, the package statement in the .proto file, if present. - // - Otherwise, the basename of the .proto file, without extension. - optional string go_package = 11; - - - - // Should generic services be generated in each language? "Generic" services - // are not specific to any particular RPC system. They are generated by the - // main code generators in each language (without additional plugins). - // Generic services were the only kind of service generation supported by - // early versions of google.protobuf. - // - // Generic services are now considered deprecated in favor of using plugins - // that generate code specific to your particular RPC system. Therefore, - // these default to false. Old code which depends on generic services should - // explicitly set them to true. - optional bool cc_generic_services = 16 [default=false]; - optional bool java_generic_services = 17 [default=false]; - optional bool py_generic_services = 18 [default=false]; - optional bool php_generic_services = 42 [default=false]; - - // Is this file deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for everything in the file, or it will be completely ignored; in the very - // least, this is a formalization for deprecating files. - optional bool deprecated = 23 [default=false]; - - // Enables the use of arenas for the proto messages in this file. This applies - // only to generated classes for C++. - optional bool cc_enable_arenas = 31 [default=false]; - - - // Sets the objective c class prefix which is prepended to all objective c - // generated classes from this .proto. There is no default. - optional string objc_class_prefix = 36; - - // Namespace for generated classes; defaults to the package. - optional string csharp_namespace = 37; - - // By default Swift generators will take the proto package and CamelCase it - // replacing '.' with underscore and use that to prefix the types/symbols - // defined. When this options is provided, they will use this value instead - // to prefix the types/symbols defined. - optional string swift_prefix = 39; - - // Sets the php class prefix which is prepended to all php generated classes - // from this .proto. Default is empty. - optional string php_class_prefix = 40; - - // Use this option to change the namespace of php generated classes. Default - // is empty. When this option is empty, the package name will be used for - // determining the namespace. - optional string php_namespace = 41; - - - // Use this option to change the namespace of php generated metadata classes. - // Default is empty. When this option is empty, the proto file name will be used - // for determining the namespace. - optional string php_metadata_namespace = 44; - - // Use this option to change the package of ruby generated classes. Default - // is empty. When this option is not set, the package name will be used for - // determining the ruby package. - optional string ruby_package = 45; - - // The parser stores options it doesn't recognize here. - // See the documentation for the "Options" section above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. - // See the documentation for the "Options" section above. - extensions 1000 to max; - - reserved 38; -} - -message MessageOptions { - // Set true to use the old proto1 MessageSet wire format for extensions. - // This is provided for backwards-compatibility with the MessageSet wire - // format. You should not use this for any other reason: It's less - // efficient, has fewer features, and is more complicated. - // - // The message must be defined exactly as follows: - // message Foo { - // option message_set_wire_format = true; - // extensions 4 to max; - // } - // Note that the message cannot have any defined fields; MessageSets only - // have extensions. - // - // All extensions of your type must be singular messages; e.g. they cannot - // be int32s, enums, or repeated messages. - // - // Because this is an option, the above two restrictions are not enforced by - // the protocol compiler. - optional bool message_set_wire_format = 1 [default=false]; - - // Disables the generation of the standard "descriptor()" accessor, which can - // conflict with a field of the same name. This is meant to make migration - // from proto1 easier; new code should avoid fields named "descriptor". - optional bool no_standard_descriptor_accessor = 2 [default=false]; - - // Is this message deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the message, or it will be completely ignored; in the very least, - // this is a formalization for deprecating messages. - optional bool deprecated = 3 [default=false]; - - // Whether the message is an automatically generated map entry type for the - // maps field. - // - // For maps fields: - // map<KeyType, ValueType> map_field = 1; - // The parsed descriptor looks like: - // message MapFieldEntry { - // option map_entry = true; - // optional KeyType key = 1; - // optional ValueType value = 2; - // } - // repeated MapFieldEntry map_field = 1; - // - // Implementations may choose not to generate the map_entry=true message, but - // use a native map in the target language to hold the keys and values. - // The reflection APIs in such implementions still need to work as - // if the field is a repeated message field. - // - // NOTE: Do not set the option in .proto files. Always use the maps syntax - // instead. The option should only be implicitly set by the proto compiler - // parser. - optional bool map_entry = 7; - - reserved 8; // javalite_serializable - reserved 9; // javanano_as_lite - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message FieldOptions { - // The ctype option instructs the C++ code generator to use a different - // representation of the field than it normally would. See the specific - // options below. This option is not yet implemented in the open source - // release -- sorry, we'll try to include it in a future version! - optional CType ctype = 1 [default = STRING]; - enum CType { - // Default mode. - STRING = 0; - - CORD = 1; - - STRING_PIECE = 2; - } - // The packed option can be enabled for repeated primitive fields to enable - // a more efficient representation on the wire. Rather than repeatedly - // writing the tag and type for each element, the entire array is encoded as - // a single length-delimited blob. In proto3, only explicit setting it to - // false will avoid using packed encoding. - optional bool packed = 2; - - // The jstype option determines the JavaScript type used for values of the - // field. The option is permitted only for 64 bit integral and fixed types - // (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING - // is represented as JavaScript string, which avoids loss of precision that - // can happen when a large value is converted to a floating point JavaScript. - // Specifying JS_NUMBER for the jstype causes the generated JavaScript code to - // use the JavaScript "number" type. The behavior of the default option - // JS_NORMAL is implementation dependent. - // - // This option is an enum to permit additional types to be added, e.g. - // goog.math.Integer. - optional JSType jstype = 6 [default = JS_NORMAL]; - enum JSType { - // Use the default type. - JS_NORMAL = 0; - - // Use JavaScript strings. - JS_STRING = 1; - - // Use JavaScript numbers. - JS_NUMBER = 2; - } - - // Should this field be parsed lazily? Lazy applies only to message-type - // fields. It means that when the outer message is initially parsed, the - // inner message's contents will not be parsed but instead stored in encoded - // form. The inner message will actually be parsed when it is first accessed. - // - // This is only a hint. Implementations are free to choose whether to use - // eager or lazy parsing regardless of the value of this option. However, - // setting this option true suggests that the protocol author believes that - // using lazy parsing on this field is worth the additional bookkeeping - // overhead typically needed to implement it. - // - // This option does not affect the public interface of any generated code; - // all method signatures remain the same. Furthermore, thread-safety of the - // interface is not affected by this option; const methods remain safe to - // call from multiple threads concurrently, while non-const methods continue - // to require exclusive access. - // - // - // Note that implementations may choose not to check required fields within - // a lazy sub-message. That is, calling IsInitialized() on the outer message - // may return true even if the inner message has missing required fields. - // This is necessary because otherwise the inner message would have to be - // parsed in order to perform the check, defeating the purpose of lazy - // parsing. An implementation which chooses not to check required fields - // must be consistent about it. That is, for any particular sub-message, the - // implementation must either *always* check its required fields, or *never* - // check its required fields, regardless of whether or not the message has - // been parsed. - optional bool lazy = 5 [default=false]; - - // Is this field deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for accessors, or it will be completely ignored; in the very least, this - // is a formalization for deprecating fields. - optional bool deprecated = 3 [default=false]; - - // For Google-internal migration only. Do not use. - optional bool weak = 10 [default=false]; - - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; - - reserved 4; // removed jtype -} - -message OneofOptions { - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message EnumOptions { - - // Set this option to true to allow mapping different tag names to the same - // value. - optional bool allow_alias = 2; - - // Is this enum deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the enum, or it will be completely ignored; in the very least, this - // is a formalization for deprecating enums. - optional bool deprecated = 3 [default=false]; - - reserved 5; // javanano_as_lite - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message EnumValueOptions { - // Is this enum value deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the enum value, or it will be completely ignored; in the very least, - // this is a formalization for deprecating enum values. - optional bool deprecated = 1 [default=false]; - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message ServiceOptions { - - // Note: Field numbers 1 through 32 are reserved for Google's internal RPC - // framework. We apologize for hoarding these numbers to ourselves, but - // we were already using them long before we decided to release Protocol - // Buffers. - - // Is this service deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the service, or it will be completely ignored; in the very least, - // this is a formalization for deprecating services. - optional bool deprecated = 33 [default=false]; - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message MethodOptions { - - // Note: Field numbers 1 through 32 are reserved for Google's internal RPC - // framework. We apologize for hoarding these numbers to ourselves, but - // we were already using them long before we decided to release Protocol - // Buffers. - - // Is this method deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the method, or it will be completely ignored; in the very least, - // this is a formalization for deprecating methods. - optional bool deprecated = 33 [default=false]; - - // Is this method side-effect-free (or safe in HTTP parlance), or idempotent, - // or neither? HTTP based RPC implementation may choose GET verb for safe - // methods, and PUT verb for idempotent methods instead of the default POST. - enum IdempotencyLevel { - IDEMPOTENCY_UNKNOWN = 0; - NO_SIDE_EFFECTS = 1; // implies idempotent - IDEMPOTENT = 2; // idempotent, but may have side effects - } - optional IdempotencyLevel idempotency_level = - 34 [default=IDEMPOTENCY_UNKNOWN]; - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - - -// A message representing a option the parser does not recognize. This only -// appears in options protos created by the compiler::Parser class. -// DescriptorPool resolves these when building Descriptor objects. Therefore, -// options protos in descriptor objects (e.g. returned by Descriptor::options(), -// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions -// in them. -message UninterpretedOption { - // The name of the uninterpreted option. Each string represents a segment in - // a dot-separated name. is_extension is true iff a segment represents an - // extension (denoted with parentheses in options specs in .proto files). - // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents - // "foo.(bar.baz).qux". - message NamePart { - required string name_part = 1; - required bool is_extension = 2; - } - repeated NamePart name = 2; - - // The value of the uninterpreted option, in whatever type the tokenizer - // identified it as during parsing. Exactly one of these should be set. - optional string identifier_value = 3; - optional uint64 positive_int_value = 4; - optional int64 negative_int_value = 5; - optional double double_value = 6; - optional bytes string_value = 7; - optional string aggregate_value = 8; -} - -// =================================================================== -// Optional source code info - -// Encapsulates information about the original source file from which a -// FileDescriptorProto was generated. -message SourceCodeInfo { - // A Location identifies a piece of source code in a .proto file which - // corresponds to a particular definition. This information is intended - // to be useful to IDEs, code indexers, documentation generators, and similar - // tools. - // - // For example, say we have a file like: - // message Foo { - // optional string foo = 1; - // } - // Let's look at just the field definition: - // optional string foo = 1; - // ^ ^^ ^^ ^ ^^^ - // a bc de f ghi - // We have the following locations: - // span path represents - // [a,i) [ 4, 0, 2, 0 ] The whole field definition. - // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional). - // [c,d) [ 4, 0, 2, 0, 5 ] The type (string). - // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo). - // [g,h) [ 4, 0, 2, 0, 3 ] The number (1). - // - // Notes: - // - A location may refer to a repeated field itself (i.e. not to any - // particular index within it). This is used whenever a set of elements are - // logically enclosed in a single code segment. For example, an entire - // extend block (possibly containing multiple extension definitions) will - // have an outer location whose path refers to the "extensions" repeated - // field without an index. - // - Multiple locations may have the same path. This happens when a single - // logical declaration is spread out across multiple places. The most - // obvious example is the "extend" block again -- there may be multiple - // extend blocks in the same scope, each of which will have the same path. - // - A location's span is not always a subset of its parent's span. For - // example, the "extendee" of an extension declaration appears at the - // beginning of the "extend" block and is shared by all extensions within - // the block. - // - Just because a location's span is a subset of some other location's span - // does not mean that it is a descendent. For example, a "group" defines - // both a type and a field in a single declaration. Thus, the locations - // corresponding to the type and field and their components will overlap. - // - Code which tries to interpret locations should probably be designed to - // ignore those that it doesn't understand, as more types of locations could - // be recorded in the future. - repeated Location location = 1; - message Location { - // Identifies which part of the FileDescriptorProto was defined at this - // location. - // - // Each element is a field number or an index. They form a path from - // the root FileDescriptorProto to the place where the definition. For - // example, this path: - // [ 4, 3, 2, 7, 1 ] - // refers to: - // file.message_type(3) // 4, 3 - // .field(7) // 2, 7 - // .name() // 1 - // This is because FileDescriptorProto.message_type has field number 4: - // repeated DescriptorProto message_type = 4; - // and DescriptorProto.field has field number 2: - // repeated FieldDescriptorProto field = 2; - // and FieldDescriptorProto.name has field number 1: - // optional string name = 1; - // - // Thus, the above path gives the location of a field name. If we removed - // the last element: - // [ 4, 3, 2, 7 ] - // this path refers to the whole field declaration (from the beginning - // of the label to the terminating semicolon). - repeated int32 path = 1 [packed=true]; - - // Always has exactly three or four elements: start line, start column, - // end line (optional, otherwise assumed same as start line), end column. - // These are packed into a single field for efficiency. Note that line - // and column numbers are zero-based -- typically you will want to add - // 1 to each before displaying to a user. - repeated int32 span = 2 [packed=true]; - - // If this SourceCodeInfo represents a complete declaration, these are any - // comments appearing before and after the declaration which appear to be - // attached to the declaration. - // - // A series of line comments appearing on consecutive lines, with no other - // tokens appearing on those lines, will be treated as a single comment. - // - // leading_detached_comments will keep paragraphs of comments that appear - // before (but not connected to) the current element. Each paragraph, - // separated by empty lines, will be one comment element in the repeated - // field. - // - // Only the comment content is provided; comment markers (e.g. //) are - // stripped out. For block comments, leading whitespace and an asterisk - // will be stripped from the beginning of each line other than the first. - // Newlines are included in the output. - // - // Examples: - // - // optional int32 foo = 1; // Comment attached to foo. - // // Comment attached to bar. - // optional int32 bar = 2; - // - // optional string baz = 3; - // // Comment attached to baz. - // // Another line attached to baz. - // - // // Comment attached to qux. - // // - // // Another line attached to qux. - // optional double qux = 4; - // - // // Detached comment for corge. This is not leading or trailing comments - // // to qux or corge because there are blank lines separating it from - // // both. - // - // // Detached comment for corge paragraph 2. - // - // optional string corge = 5; - // /* Block comment attached - // * to corge. Leading asterisks - // * will be removed. */ - // /* Block comment attached to - // * grault. */ - // optional int32 grault = 6; - // - // // ignored detached comments. - optional string leading_comments = 3; - optional string trailing_comments = 4; - repeated string leading_detached_comments = 6; - } -} - -// Describes the relationship between generated code and its original source -// file. A GeneratedCodeInfo message is associated with only one generated -// source file, but may contain references to different source .proto files. -message GeneratedCodeInfo { - // An Annotation connects some span of text in generated code to an element - // of its generating .proto file. - repeated Annotation annotation = 1; - message Annotation { - // Identifies the element in the original source .proto file. This field - // is formatted the same as SourceCodeInfo.Location.path. - repeated int32 path = 1 [packed=true]; - - // Identifies the filesystem path to the original source .proto. - optional string source_file = 2; - - // Identifies the starting offset in bytes in the generated code - // that relates to the identified object. - optional int32 begin = 3; - - // Identifies the ending offset in bytes in the generated code that - // relates to the identified offset. The end offset should be one past - // the last relevant byte (so the length of the text = end - begin). - optional int32 end = 4; - } -} diff --git a/lib/gogo.proto b/lib/gogo.proto deleted file mode 100644 index caab5793..00000000 --- a/lib/gogo.proto +++ /dev/null @@ -1,116 +0,0 @@ -syntax = "proto2"; -package gogoproto; - -import "descriptor.proto"; - -option java_package = "com.google.protobuf"; -option java_outer_classname = "GoGoProtos"; -option go_package = "github.com/gogo/protobuf/gogoproto"; - -extend google.protobuf.EnumOptions { - optional bool goproto_enum_prefix = 62001; - optional bool goproto_enum_stringer = 62021; - optional bool enum_stringer = 62022; - optional string enum_customname = 62023; - optional bool enumdecl = 62024; -} - -extend google.protobuf.EnumValueOptions { - optional string enumvalue_customname = 66001; -} - -extend google.protobuf.FileOptions { - optional bool goproto_getters_all = 63001; - optional bool goproto_enum_prefix_all = 63002; - optional bool goproto_stringer_all = 63003; - optional bool verbose_equal_all = 63004; - optional bool face_all = 63005; - optional bool gostring_all = 63006; - optional bool populate_all = 63007; - optional bool stringer_all = 63008; - optional bool onlyone_all = 63009; - - optional bool equal_all = 63013; - optional bool description_all = 63014; - optional bool testgen_all = 63015; - optional bool benchgen_all = 63016; - optional bool marshaler_all = 63017; - optional bool unmarshaler_all = 63018; - optional bool stable_marshaler_all = 63019; - - optional bool sizer_all = 63020; - - optional bool goproto_enum_stringer_all = 63021; - optional bool enum_stringer_all = 63022; - - optional bool unsafe_marshaler_all = 63023; - optional bool unsafe_unmarshaler_all = 63024; - - optional bool goproto_extensions_map_all = 63025; - optional bool goproto_unrecognized_all = 63026; - optional bool gogoproto_import = 63027; - optional bool protosizer_all = 63028; - optional bool compare_all = 63029; - optional bool typedecl_all = 63030; - optional bool enumdecl_all = 63031; - - optional bool goproto_registration = 63032; - optional bool messagename_all = 63033; - - optional bool goproto_sizecache_all = 63034; - optional bool goproto_unkeyed_all = 63035; -} - -extend google.protobuf.MessageOptions { - optional bool goproto_getters = 64001; - optional bool goproto_stringer = 64003; - optional bool verbose_equal = 64004; - optional bool face = 64005; - optional bool gostring = 64006; - optional bool populate = 64007; - optional bool stringer = 67008; - optional bool onlyone = 64009; - - optional bool equal = 64013; - optional bool description = 64014; - optional bool testgen = 64015; - optional bool benchgen = 64016; - optional bool marshaler = 64017; - optional bool unmarshaler = 64018; - optional bool stable_marshaler = 64019; - - optional bool sizer = 64020; - - optional bool unsafe_marshaler = 64023; - optional bool unsafe_unmarshaler = 64024; - - optional bool goproto_extensions_map = 64025; - optional bool goproto_unrecognized = 64026; - - optional bool protosizer = 64028; - optional bool compare = 64029; - - optional bool typedecl = 64030; - - optional bool messagename = 64033; - - optional bool goproto_sizecache = 64034; - optional bool goproto_unkeyed = 64035; -} - -extend google.protobuf.FieldOptions { - optional bool nullable = 65001; - optional bool embed = 65002; - optional string customtype = 65003; - optional string customname = 65004; - optional string jsontag = 65005; - optional string moretags = 65006; - optional string casttype = 65007; - optional string castkey = 65008; - optional string castvalue = 65009; - - optional bool stdtime = 65010; - optional bool stdduration = 65011; - optional bool wktpointer = 65012; - -} diff --git a/lib/handlebars-helpers/index.js b/lib/handlebars-helpers/index.js deleted file mode 100644 index fc45b6d9..00000000 --- a/lib/handlebars-helpers/index.js +++ /dev/null @@ -1,57 +0,0 @@ -/*! - * handlebars-helpers <https://github.com/helpers/handlebars-helpers> - * - * Copyright (c) 2013-2017, Jon Schlinkert, Brian Woodward. - * Released under the MIT License. - */ - -'use strict'; - -var lib = { - math: require('./math'), - string: require('./string'), -} - -/** - * Expose helpers - */ - -module.exports = function helpers(groups, options) { - if (typeof groups === 'string') { - groups = [groups]; - } else if (!Array.isArray(groups)) { - options = groups; - groups = null; - } - - options = options || {}; - const hbs = options.handlebars || options.hbs || require('handlebars'); - module.exports.handlebars = hbs; - - if (groups) { - groups.forEach(function(key) { - hbs.registerHelper(lib[key]); - }); - } else { - Object.values(lib).forEach(function(group) { - hbs.registerHelper(group); - }); - } - - return hbs.helpers; -}; - -/** - * Expose helper groups - */ - -Object.entries(lib).forEach(function(key_group) { - const [key, group] = key_group; - module.exports[key] = function(options) { - options = options || {}; - let hbs = options.handlebars || options.hbs || require('handlebars'); - module.exports.handlebars = hbs; - hbs.registerHelper(group); - return hbs.helpers; - }; -}); diff --git a/lib/handlebars-helpers/math.js b/lib/handlebars-helpers/math.js deleted file mode 100644 index f4c138b9..00000000 --- a/lib/handlebars-helpers/math.js +++ /dev/null @@ -1,288 +0,0 @@ -'use strict'; - -var isNumber = require('is-number'); -var utils = require('./utils'); -var helpers = module.exports; - -/** - * Return the magnitude of `a`. - * - * @param {Number} `a` - * @return {Number} - * @api public - */ - -helpers.abs = function(num) { - if (!isNumber(num)) { - throw new TypeError('expected a number'); - } - return Math.abs(num); -}; - -/** - * Return the sum of `a` plus `b`. - * - * @param {Number} `a` - * @param {Number} `b` - * @return {Number} - * @api public - */ - -helpers.add = function(a, b) { - if (isNumber(a) && isNumber(b)) { - return Number(a) + Number(b); - } - if (typeof a === 'string' && typeof b === 'string') { - return a + b; - } - return ''; -}; - -/** - * Returns the average of all numbers in the given array. - * - * ```handlebars - * {{avg "[1, 2, 3, 4, 5]"}} - * <!-- results in: '3' --> - * ``` - * - * @param {Array} `array` Array of numbers to add up. - * @return {Number} - * @api public - */ - -helpers.avg = function() { - var args = [].concat.apply([], arguments); - // remove handlebars options object - args.pop(); - return helpers.sum(args) / args.length; -}; - -/** - * Get the `Math.ceil()` of the given value. - * - * @param {Number} `value` - * @return {Number} - * @api public - */ - -helpers.ceil = function(num) { - if (!isNumber(num)) { - throw new TypeError('expected a number'); - } - return Math.ceil(num); -}; - -/** - * Divide `a` by `b` - * - * @param {Number} `a` numerator - * @param {Number} `b` denominator - * @api public - */ - -helpers.divide = function(a, b) { - if (!isNumber(a)) { - throw new TypeError('expected the first argument to be a number'); - } - if (!isNumber(b)) { - throw new TypeError('expected the second argument to be a number'); - } - return Number(a) / Number(b); -}; - -/** - * Get the `Math.floor()` of the given value. - * - * @param {Number} `value` - * @return {Number} - * @api public - */ - -helpers.floor = function(num) { - if (!isNumber(num)) { - throw new TypeError('expected a number'); - } - return Math.floor(num); -}; - -/** - * Return the difference of `a` minus `b`. - * - * @param {Number} `a` - * @param {Number} `b` - * @alias subtract - * @api public - */ - -helpers.minus = function(a, b) { - if (!isNumber(a)) { - throw new TypeError('expected the first argument to be a number'); - } - if (!isNumber(b)) { - throw new TypeError('expected the second argument to be a number'); - } - return Number(a) - Number(b); -}; - -/** - * Get the remainder of a division operation. - * - * @param {Number} `a` - * @param {Number} `b` - * @return {Number} - * @api public - */ - -helpers.modulo = function(a, b) { - if (!isNumber(a)) { - throw new TypeError('expected the first argument to be a number'); - } - if (!isNumber(b)) { - throw new TypeError('expected the second argument to be a number'); - } - return Number(a) % Number(b); -}; - -/** - * Return the product of `a` times `b`. - * - * @param {Number} `a` factor - * @param {Number} `b` multiplier - * @return {Number} - * @alias times - * @api public - */ - -helpers.multiply = function(a, b) { - if (!isNumber(a)) { - throw new TypeError('expected the first argument to be a number'); - } - if (!isNumber(b)) { - throw new TypeError('expected the second argument to be a number'); - } - return Number(a) * Number(b); -}; - -/** - * Add `a` by `b`. - * - * @param {Number} `a` factor - * @param {Number} `b` multiplier - * @api public - */ - -helpers.plus = function(a, b) { - if (!isNumber(a)) { - throw new TypeError('expected the first argument to be a number'); - } - if (!isNumber(b)) { - throw new TypeError('expected the second argument to be a number'); - } - return Number(a) + Number(b); -}; - -/** - * Generate a random number between two values - * - * @param {Number} `min` - * @param {Number} `max` - * @return {String} - * @api public - */ - -helpers.random = function(min, max) { - if (!isNumber(min)) { - throw new TypeError('expected minimum to be a number'); - } - if (!isNumber(max)) { - throw new TypeError('expected maximum to be a number'); - } - return utils.random(min, max); -}; - -/** - * Get the remainder when `a` is divided by `b`. - * - * @param {Number} `a` a - * @param {Number} `b` b - * @api public - */ - -helpers.remainder = function(a, b) { - return a % b; -}; - -/** - * Round the given number. - * - * @param {Number} `number` - * @return {Number} - * @api public - */ - -helpers.round = function(num) { - if (!isNumber(num)) { - throw new TypeError('expected a number'); - } - return Math.round(num); -}; - -/** - * Return the product of `a` minus `b`. - * - * @param {Number} `a` - * @param {Number} `b` - * @return {Number} - * @alias minus - * @api public - */ - -helpers.subtract = function(a, b) { - if (!isNumber(a)) { - throw new TypeError('expected the first argument to be a number'); - } - if (!isNumber(b)) { - throw new TypeError('expected the second argument to be a number'); - } - return Number(a) - Number(b); -}; - -/** - * Returns the sum of all numbers in the given array. - * - * ```handlebars - * {{sum "[1, 2, 3, 4, 5]"}} - * <!-- results in: '15' --> - * ``` - * @param {Array} `array` Array of numbers to add up. - * @return {Number} - * @api public - */ - -helpers.sum = function() { - var args = [].concat.apply([], arguments); - var len = args.length; - var sum = 0; - - while (len--) { - if (utils.isNumber(args[len])) { - sum += Number(args[len]); - } - } - return sum; -}; - -/** - * Multiply number `a` by number `b`. - * - * @param {Number} `a` factor - * @param {Number} `b` multiplier - * @return {Number} - * @alias multiply - * @api public - */ - -helpers.times = function() { - return helpers.multiply.apply(this, arguments); -}; diff --git a/lib/handlebars-helpers/string.js b/lib/handlebars-helpers/string.js deleted file mode 100644 index 5f079766..00000000 --- a/lib/handlebars-helpers/string.js +++ /dev/null @@ -1,769 +0,0 @@ -'use strict'; - -var isNumber = require('is-number'); -var util = require('handlebars-utils'); -var utils = require('./utils'); -var helpers = module.exports; - -/** - * Append the specified `suffix` to the given string. - * - * ```handlebars - * <!-- given that "item.stem" is "foo" --> - * {{append item.stem ".html"}} - * <!-- results in: 'foo.html' --> - * ``` - * @param {String} `str` - * @param {String} `suffix` - * @return {String} - * @api public - */ - -helpers.append = function(str, suffix) { - if (typeof str === 'string' && typeof suffix === 'string') { - return str + suffix; - } - return str; -}; - -/** - * camelCase the characters in the given `string`. - * - * ```handlebars - * {{camelcase "foo bar baz"}}; - * <!-- results in: 'fooBarBaz' --> - * ``` - * @param {String} `string` The string to camelcase. - * @return {String} - * @api public - */ - -helpers.camelcase = function(str) { - if (!util.isString(str)) return ''; - return utils.changecase(str, function(ch) { - return ch.toUpperCase(); - }); -}; - -/** - * Capitalize the first word in a sentence. - * - * ```handlebars - * {{capitalize "foo bar baz"}} - * <!-- results in: "Foo bar baz" --> - * ``` - * @param {String} `str` - * @return {String} - * @api public - */ - -helpers.capitalize = function(str) { - if (!util.isString(str)) return ''; - return str.charAt(0).toUpperCase() + str.slice(1); -}; - -/** - * Capitalize all words in a string. - * - * ```handlebars - * {{capitalizeAll "foo bar baz"}} - * <!-- results in: "Foo Bar Baz" --> - * ``` - * @param {String} `str` - * @return {String} - * @api public - */ - -helpers.capitalizeAll = function(str) { - if (!util.isString(str)) return ''; - if (util.isString(str)) { - return str.replace(/\w\S*/g, function(word) { - return helpers.capitalize(word); - }); - } -}; - -/** - * Center a string using non-breaking spaces - * - * @param {String} `str` - * @param {String} `spaces` - * @return {String} - * @api public - */ - -helpers.center = function(str, spaces) { - if (!util.isString(str)) return ''; - var space = ''; - var i = 0; - while (i < spaces) { - space += ' '; - i++; - } - return space + str + space; -}; - -/** - * Like trim, but removes both extraneous whitespace **and - * non-word characters** from the beginning and end of a string. - * - * ```handlebars - * {{chop "_ABC_"}} - * <!-- results in: 'ABC' --> - * - * {{chop "-ABC-"}} - * <!-- results in: 'ABC' --> - * - * {{chop " ABC "}} - * <!-- results in: 'ABC' --> - * ``` - * @param {String} `string` The string to chop. - * @return {String} - * @api public - */ - -helpers.chop = function(str) { - if (!util.isString(str)) return ''; - return utils.chop(str); -}; - -/** - * dash-case the characters in `string`. Replaces non-word - * characters and periods with hyphens. - * - * ```handlebars - * {{dashcase "a-b-c d_e"}} - * <!-- results in: 'a-b-c-d-e' --> - * ``` - * @param {String} `string` - * @return {String} - * @api public - */ - -helpers.dashcase = function(str) { - if (!util.isString(str)) return ''; - return utils.changecase(str, function(ch) { - return '-' + ch; - }); -}; - -/** - * dot.case the characters in `string`. - * - * ```handlebars - * {{dotcase "a-b-c d_e"}} - * <!-- results in: 'a.b.c.d.e' --> - * ``` - * @param {String} `string` - * @return {String} - * @api public - */ - -helpers.dotcase = function(str) { - if (!util.isString(str)) return ''; - return utils.changecase(str, function(ch) { - return '.' + ch; - }); -}; - -/** - * Lowercase all of the characters in the given string. Alias for [lowercase](#lowercase). - * - * ```handlebars - * {{downcase "aBcDeF"}} - * <!-- results in: 'abcdef' --> - * ``` - * @param {String} `string` - * @return {String} - * @alias lowercase - * @api public - */ - -helpers.downcase = function() { - return helpers.lowercase.apply(this, arguments); -}; - -/** - * Truncates a string to the specified `length`, and appends - * it with an elipsis, `…`. - * - * ```handlebars - * {{ellipsis (sanitize "<span>foo bar baz</span>"), 7}} - * <!-- results in: 'foo bar…' --> - * {{ellipsis "foo bar baz", 7}} - * <!-- results in: 'foo bar…' --> - * ``` - * @param {String} `str` - * @param {Number} `length` The desired length of the returned string. - * @return {String} The truncated string. - * @api public - */ - -helpers.ellipsis = function(str, limit) { - if (util.isString(str)) { - if (str.length <= limit) { - return str; - } - return helpers.truncate(str, limit) + '…'; - } -}; - -/** - * Replace spaces in a string with hyphens. - * - * ```handlebars - * {{hyphenate "foo bar baz qux"}} - * <!-- results in: "foo-bar-baz-qux" --> - * ``` - * @param {String} `str` - * @return {String} - * @api public - */ - -helpers.hyphenate = function(str) { - if (!util.isString(str)) return ''; - return str.split(' ').join('-'); -}; - -/** - * Return true if `value` is a string. - * - * ```handlebars - * {{isString "foo"}} - * <!-- results in: 'true' --> - * ``` - * @param {String} `value` - * @return {Boolean} - * @api public - */ - -helpers.isString = function(value) { - return typeof value === 'string'; -}; - -/** - * Lowercase all characters in the given string. - * - * ```handlebars - * {{lowercase "Foo BAR baZ"}} - * <!-- results in: 'foo bar baz' --> - * ``` - * @param {String} `str` - * @return {String} - * @api public - */ - -helpers.lowercase = function(str) { - if (util.isObject(str) && str.fn) { - return str.fn(this).toLowerCase(); - } - if (!util.isString(str)) return ''; - return str.toLowerCase(); -}; - -/** - * Return the number of occurrences of `substring` within the - * given `string`. - * - * ```handlebars - * {{occurrences "foo bar foo bar baz" "foo"}} - * <!-- results in: 2 --> - * ``` - * @param {String} `str` - * @param {String} `substring` - * @return {Number} Number of occurrences - * @api public - */ - -helpers.occurrences = function(str, substring) { - if (!util.isString(str)) return ''; - var len = substring.length; - var pos = 0; - var n = 0; - - while ((pos = str.indexOf(substring, pos)) > -1) { - n++; - pos += len; - } - return n; -}; - -/** - * PascalCase the characters in `string`. - * - * ```handlebars - * {{pascalcase "foo bar baz"}} - * <!-- results in: 'FooBarBaz' --> - * ``` - * @param {String} `string` - * @return {String} - * @api public - */ - -helpers.pascalcase = function(str) { - if (!util.isString(str)) return ''; - str = utils.changecase(str, function(ch) { - return ch.toUpperCase(); - }); - return str.charAt(0).toUpperCase() + str.slice(1); -}; - -/** - * path/case the characters in `string`. - * - * ```handlebars - * {{pathcase "a-b-c d_e"}} - * <!-- results in: 'a/b/c/d/e' --> - * ``` - * @param {String} `string` - * @return {String} - * @api public - */ - -helpers.pathcase = function(str) { - if (!util.isString(str)) return ''; - return utils.changecase(str, function(ch) { - return '/' + ch; - }); -}; - -/** - * Replace spaces in the given string with pluses. - * - * ```handlebars - * {{plusify "foo bar baz"}} - * <!-- results in: 'foo+bar+baz' --> - * ``` - * @param {String} `str` The input string - * @return {String} Input string with spaces replaced by plus signs - * @source Stephen Way <https://github.com/stephenway> - * @api public - */ - -helpers.plusify = function(str, ch) { - if (!util.isString(str)) return ''; - if (!util.isString(ch)) ch = ' '; - return str.split(ch).join('+'); -}; - -/** - * Prepends the given `string` with the specified `prefix`. - * - * ```handlebars - * <!-- given that "val" is "bar" --> - * {{prepend val "foo-"}} - * <!-- results in: 'foo-bar' --> - * ``` - * @param {String} `str` - * @param {String} `prefix` - * @return {String} - * @api public - */ - -helpers.prepend = function(str, prefix) { - return typeof str === 'string' && typeof prefix === 'string' - ? (prefix + str) - : str; -}; - -/** - * Render a block without processing mustache templates inside the block. - * - * ```handlebars - * {{{{#raw}}}} - * {{foo}} - * {{{{/raw}}}} - * <!-- results in: '{{foo}}' --> - * ``` - * - * @param {Object} `options` - * @return {String} - * @block - * @api public - */ - -helpers.raw = function(options) { - var str = options.fn(); - var opts = util.options(this, options); - if (opts.escape !== false) { - var idx = 0; - while (((idx = str.indexOf('{{', idx)) !== -1)) { - if (str[idx - 1] !== '\\') { - str = str.slice(0, idx) + '\\' + str.slice(idx); - } - idx += 3; - } - } - return str; -}; - -/** - * Remove all occurrences of `substring` from the given `str`. - * - * ```handlebars - * {{remove "a b a b a b" "a "}} - * <!-- results in: 'b b b' --> - * ``` - * @param {String} `str` - * @param {String} `substring` - * @return {String} - * @api public - */ - -helpers.remove = function(str, ch) { - if (!util.isString(str)) return ''; - if (!util.isString(ch)) return str; - return str.split(ch).join(''); -}; - -/** - * Remove the first occurrence of `substring` from the given `str`. - * - * ```handlebars - * {{remove "a b a b a b" "a"}} - * <!-- results in: ' b a b a b' --> - * ``` - * @param {String} `str` - * @param {String} `substring` - * @return {String} - * @api public - */ - -helpers.removeFirst = function(str, ch) { - if (!util.isString(str)) return ''; - if (!util.isString(ch)) return str; - return str.replace(ch, ''); -}; - -/** - * Replace all occurrences of substring `a` with substring `b`. - * - * ```handlebars - * {{replace "a b a b a b" "a" "z"}} - * <!-- results in: 'z b z b z b' --> - * ``` - * @param {String} `str` - * @param {String} `a` - * @param {String} `b` - * @return {String} - * @api public - */ - -helpers.replace = function(str, a, b) { - if (!util.isString(str)) return ''; - if (!util.isString(a)) return str; - if (!util.isString(b)) b = ''; - return str.split(a).join(b); -}; - -/** - * Replace the first occurrence of substring `a` with substring `b`. - * - * ```handlebars - * {{replace "a b a b a b" "a" "z"}} - * <!-- results in: 'z b a b a b' --> - * ``` - * @param {String} `str` - * @param {String} `a` - * @param {String} `b` - * @return {String} - * @api public - */ - -helpers.replaceFirst = function(str, a, b) { - if (!util.isString(str)) return ''; - if (!util.isString(a)) return str; - if (!util.isString(b)) b = ''; - return str.replace(a, b); -}; - -/** - * Reverse a string. - * - * ```handlebars - * {{reverse "abcde"}} - * <!-- results in: 'edcba' --> - * ``` - * @param {String} `str` - * @return {String} - * @api public - */ - -helpers.reverse = function(str) { - if (!util.isString(str)) return ''; - return str.split('').reverse().join(''); -}; - -/** - * Sentence case the given string - * - * ```handlebars - * {{sentence "hello world. goodbye world."}} - * <!-- results in: 'Hello world. Goodbye world.' --> - * ``` - * @param {String} `str` - * @return {String} - * @api public - */ - -helpers.sentence = function(str) { - if (!util.isString(str)) return ''; - return str.replace(/((?:\S[^\.\?\!]*)[\.\?\!]*)/g, function(txt) { - return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); - }); -}; - -/** - * snake_case the characters in the given `string`. - * - * ```handlebars - * {{snakecase "a-b-c d_e"}} - * <!-- results in: 'a_b_c_d_e' --> - * ``` - * @param {String} `string` - * @return {String} - * @api public - */ - -helpers.snakecase = function(str) { - if (!util.isString(str)) return ''; - return utils.changecase(str, function(ch) { - return '_' + ch; - }); -}; - -/** - * Split `string` by the given `character`. - * - * ```handlebars - * {{split "a,b,c" ","}} - * <!-- results in: ['a', 'b', 'c'] --> - * ``` - * @param {String} `string` The string to split. - * @return {String} `character` Default is an empty string. - * @api public - */ - -helpers.split = function(str, ch) { - if (!util.isString(str)) return ''; - if (!util.isString(ch)) ch = ','; - return str.split(ch); -}; - -/** - * Tests whether a string begins with the given prefix. - * - * ```handlebars - * {{#startsWith "Goodbye" "Hello, world!"}} - * Whoops - * {{else}} - * Bro, do you even hello world? - * {{/startsWith}} - * ``` - * @contributor Dan Fox <http://github.com/iamdanfox> - * @param {String} `prefix` - * @param {String} `testString` - * @param {String} `options` - * @return {String} - * @block - * @api public - */ - -helpers.startsWith = function(prefix, str, options) { - var args = [].slice.call(arguments); - options = args.pop(); - if (util.isString(str) && str.indexOf(prefix) === 0) { - return options.fn(this); - } - if (typeof options.inverse === 'function') { - return options.inverse(this); - } - return ''; -}; - -/** - * Title case the given string. - * - * ```handlebars - * {{titleize "this is title case"}} - * <!-- results in: 'This Is Title Case' --> - * ``` - * @param {String} `str` - * @return {String} - * @api public - */ - -helpers.titleize = function(str) { - if (!util.isString(str)) return ''; - var title = str.replace(/[- _]+/g, ' '); - var words = title.split(' '); - var len = words.length; - var res = []; - var i = 0; - while (len--) { - var word = words[i++]; - res.push(exports.capitalize(word)); - } - return res.join(' '); -}; - -/** - * Removes extraneous whitespace from the beginning and end - * of a string. - * - * ```handlebars - * {{trim " ABC "}} - * <!-- results in: 'ABC' --> - * ``` - * @param {String} `string` The string to trim. - * @return {String} - * @api public - */ - -helpers.trim = function(str) { - return typeof str === 'string' ? str.trim() : ''; -}; - -/** - * Removes extraneous whitespace from the beginning of a string. - * - * ```handlebars - * {{trim " ABC "}} - * <!-- results in: 'ABC ' --> - * ``` - * @param {String} `string` The string to trim. - * @return {String} - * @api public - */ - -helpers.trimLeft = function(str) { - if (util.isString(str)) { - return str.replace(/^\s+/, ''); - } -}; - -/** - * Removes extraneous whitespace from the end of a string. - * - * ```handlebars - * {{trimRight " ABC "}} - * <!-- results in: ' ABC' --> - * ``` - * @param {String} `string` The string to trim. - * @return {String} - * @api public - */ - -helpers.trimRight = function(str) { - if (util.isString(str)) { - return str.replace(/\s+$/, ''); - } -}; - -/** - * Truncate a string to the specified `length`. Also see [ellipsis](#ellipsis). - * - * ```handlebars - * truncate("foo bar baz", 7); - * <!-- results in: 'foo bar' --> - * truncate(sanitize("<span>foo bar baz</span>", 7)); - * <!-- results in: 'foo bar' --> - * ``` - * @param {String} `str` - * @param {Number} `limit` The desired length of the returned string. - * @param {String} `suffix` Optionally supply a string to use as a suffix to - * denote when the string has been truncated. Otherwise an ellipsis (`…`) will be used. - * @return {String} The truncated string. - * @api public - */ - -helpers.truncate = function(str, limit, suffix) { - if (util.isString(str)) { - if (typeof suffix !== 'string') { - suffix = ''; - } - if (str.length > limit) { - return str.slice(0, limit - suffix.length) + suffix; - } - return str; - } -}; - -/** - * Truncate a string to have the specified number of words. - * Also see [truncate](#truncate). - * - * ```handlebars - * truncateWords("foo bar baz", 1); - * <!-- results in: 'foo…' --> - * truncateWords("foo bar baz", 2); - * <!-- results in: 'foo bar…' --> - * truncateWords("foo bar baz", 3); - * <!-- results in: 'foo bar baz' --> - * ``` - * @param {String} `str` - * @param {Number} `limit` The desired length of the returned string. - * @param {String} `suffix` Optionally supply a string to use as a suffix to - * denote when the string has been truncated. - * @return {String} The truncated string. - * @api public - */ - -helpers.truncateWords = function(str, count, suffix) { - if (util.isString(str) && isNumber(count)) { - if (typeof suffix !== 'string') { - suffix = '…'; - } - - var num = Number(count); - var arr = str.split(/[ \t]/); - if (num > arr.length) { - arr = arr.slice(0, num); - } - - var val = arr.join(' ').trim(); - return val + suffix; - } -}; - -/** - * Uppercase all of the characters in the given string. Alias for [uppercase](#uppercase). - * - * ```handlebars - * {{upcase "aBcDeF"}} - * <!-- results in: 'ABCDEF' --> - * ``` - * @param {String} `string` - * @return {String} - * @alias uppercase - * @api public - */ - -helpers.upcase = function() { - return helpers.uppercase.apply(this, arguments); -}; - -/** - * Uppercase all of the characters in the given string. If used as a - * block helper it will uppercase the entire block. This helper - * does not support inverse blocks. - * - * ```handlebars - * {{uppercase "aBcDeF"}} - * <!-- results in: 'ABCDEF' --> - * ``` - * @related capitalize capitalizeAll - * @param {String} `str` The string to uppercase - * @param {Object} `options` Handlebars options object - * @return {String} - * @block - * @api public - */ - -helpers.uppercase = function(str) { - if (util.isObject(str) && str.fn) { - return str.fn(this).toUpperCase(); - } - if (!util.isString(str)) return ''; - return str.toUpperCase(); -}; diff --git a/lib/handlebars-helpers/utils.js b/lib/handlebars-helpers/utils.js deleted file mode 100644 index 33cc79b5..00000000 --- a/lib/handlebars-helpers/utils.js +++ /dev/null @@ -1,50 +0,0 @@ -const util = require('handlebars-utils'); - -let utils = {} - -utils.changecase = function(str, fn) { - if (!util.isString(str)) return ''; - if (str.length === 1) { - return str.toLowerCase(); - } - - str = utils.chop(str).toLowerCase(); - if (typeof fn !== 'function') { - fn = utils.identity; - } - - var re = /[-_.\W\s]+(\w|$)/g; - return str.replace(re, function(_, ch) { - return fn(ch); - }); -}; - -/** - * Generate a random number - * - * @param {Number} `min` - * @param {Number} `max` - * @return {Number} - * @api public - */ - -utils.random = function(min, max) { - return min + Math.floor(Math.random() * (max - min + 1)); -}; - -utils = { - ...utils, - ...require('is-number') -}; - -utils.chop = function(str) { - if (!util.isString(str)) return ''; - var re = /^[-_.\W\s]+|[-_.\W\s]+$/g; - return str.trim().replace(re, ''); -}; - -/** - * Expose `utils` - */ - -module.exports = utils; diff --git a/lib/handlers/404.js b/lib/handlers/404.js deleted file mode 100644 index daba3363..00000000 --- a/lib/handlers/404.js +++ /dev/null @@ -1,6 +0,0 @@ -function handler (req, res) { - req.log.debug('unsupported', req.url) - return res.code(404).send('404 Not Supported') -} - -module.exports = handler diff --git a/lib/handlers/alerts/common.js b/lib/handlers/alerts/common.js deleted file mode 100644 index 80f19ec8..00000000 --- a/lib/handlers/alerts/common.js +++ /dev/null @@ -1,30 +0,0 @@ -const { isEnabled } = require('../../db/alerting') -const { QrynNotFound } = require('../errors') -/** - * @param ns {Object<string, alerting.objGroup>} - * @returns {alerting.group[]} - */ -module.exports.nsToResp = (ns) => { - return Object.values(ns).map(module.exports.groupToResp) -} - -/** - * @param grp {alerting.objGroup} - * @returns {alerting.group} - */ -module.exports.groupToResp = (grp) => { - return { - ...grp, - rules: Object.values(grp.rules).map(r => { - const _r = { ...r } - delete _r._watcher - return _r - }) - } -} - -module.exports.assertEnabled = () => { - if (!isEnabled()) { - throw new QrynNotFound('Alerting not ready') - } -} diff --git a/lib/handlers/alerts/del_group.js b/lib/handlers/alerts/del_group.js deleted file mode 100644 index 49a49900..00000000 --- a/lib/handlers/alerts/del_group.js +++ /dev/null @@ -1,7 +0,0 @@ -const { dropGroup } = require('../../db/alerting') -const { assertEnabled } = require('./common') -module.exports = async (req, res) => { - assertEnabled() - await dropGroup(req.params.ns, req.params.group) - return res.code(200).send('ok') -} diff --git a/lib/handlers/alerts/del_ns.js b/lib/handlers/alerts/del_ns.js deleted file mode 100644 index 1e4b46c4..00000000 --- a/lib/handlers/alerts/del_ns.js +++ /dev/null @@ -1,7 +0,0 @@ -const { dropNs } = require('../../db/alerting') -const { assertEnabled } = require('./common') -module.exports = async (req, res) => { - assertEnabled() - await dropNs(req.params.ns) - return res.code(200).send('ok') -} diff --git a/lib/handlers/alerts/get_group.js b/lib/handlers/alerts/get_group.js deleted file mode 100644 index b74e4792..00000000 --- a/lib/handlers/alerts/get_group.js +++ /dev/null @@ -1,35 +0,0 @@ -const { getGroup } = require('../../db/alerting') -const yaml = require('yaml') -const { groupToResp, assertEnabled } = require('./common') - -const testRule = (res) => { - /** @type {alerting.group} */ - const group = { - name: 'test', - rules: [], - interval: '1s' - } - return res.header('Content-Type', 'yaml').send(yaml.stringify(group)) -} - -module.exports = (req, res) => { - assertEnabled() - if (req.params.ns === 'test' && req.params.group === 'test') { - return testRule(res) - } - const grp = getGroup(req.params.ns, req.params.group) - if (!grp) { - /** @type {alerting.group} */ - const result = { - name: req.params.group, - interval: '1s', - rules: [] - } - return res.header('Content-Type', 'yaml').send(yaml.stringify(result)) - } - for (const rul of Object.values(grp.rules)) { - delete rul.ver - } - const result = groupToResp({ ...grp }) - return res.header('Content-Type', 'yaml').send(yaml.stringify(result)) -} diff --git a/lib/handlers/alerts/get_namespace.js b/lib/handlers/alerts/get_namespace.js deleted file mode 100644 index 5af0e7e7..00000000 --- a/lib/handlers/alerts/get_namespace.js +++ /dev/null @@ -1,14 +0,0 @@ -const { getNs } = require('../../db/alerting') -const { nsToResp, assertEnabled } = require('./common') -const yaml = require('yaml') -const { QrynNotFound } = require('../errors') - -module.exports = (req, res) => { - assertEnabled() - const ns = getNs(req.params.ns) - if (!ns) { - throw QrynNotFound('Namespace not found') - } - const result = nsToResp({ ...ns }) - return res.header('Content-Type', 'yaml').send(yaml.stringify(result)) -} diff --git a/lib/handlers/alerts/get_rules.js b/lib/handlers/alerts/get_rules.js deleted file mode 100644 index 6839cc03..00000000 --- a/lib/handlers/alerts/get_rules.js +++ /dev/null @@ -1,28 +0,0 @@ -const yaml = require('yaml') -const { getAll } = require('../../db/alerting') -const { nsToResp, assertEnabled } = require('./common') - -module.exports = async (req, res) => { - assertEnabled() - /** @type {Object<string, Object<string, alerting.objGroup>>} */ - const result = { - fake: { - fake: { - name: 'fake', - rules: {} - } - }, - ...getAll() - } - for (const ns of Object.values(result)) { - for (const grp of Object.values(ns)) { - for (const rul of Object.values(grp.rules)) { - delete rul.ver - } - } - } - for (const k of Object.keys(result)) { - result[k] = nsToResp(result[k]) - } - return res.header('Content-Type', 'yaml').send(yaml.stringify(result)) -} diff --git a/lib/handlers/alerts/post_group.js b/lib/handlers/alerts/post_group.js deleted file mode 100644 index 2450e93d..00000000 --- a/lib/handlers/alerts/post_group.js +++ /dev/null @@ -1,10 +0,0 @@ -const yaml = require('yaml') -const { setGroup } = require('../../db/alerting') -const { assertEnabled } = require('./common') -module.exports = async (req, res) => { - assertEnabled() - /** @type {alerting.group} */ - const group = req.body instanceof Object ? req.body : yaml.parse(req.body) - await setGroup(req.params.ns, group) - return res.code(200).send({ msg: 'ok' }) -} diff --git a/lib/handlers/alerts/prom_get_rules.js b/lib/handlers/alerts/prom_get_rules.js deleted file mode 100644 index df024850..00000000 --- a/lib/handlers/alerts/prom_get_rules.js +++ /dev/null @@ -1,55 +0,0 @@ -const { getAll } = require('../../db/alerting') -const format = require('date-fns/formatRFC3339') -const { durationToMs } = require('../../../common') -const { assertEnabled } = require('./common') - -module.exports = (req, res) => { - assertEnabled() - const rules = getAll() - const groups = [] - for (const ns of Object.entries(rules)) { - for (const group of Object.entries(ns[1])) { - groups.push({ - name: group[0], - file: ns[0], - rules: Object.values(group[1].rules).map(rul2Res) - }) - } - } - return res.send({ - status: 'success', - data: { - groups: groups - } - }) -} - -/** - * - * @param rul {alerting.rule} - */ -const rul2Res = (rul) => { - const alerts = rul && rul._watcher && rul._watcher.getLastAlert() - ? [{ - ...rul._watcher.getLastAlert(), - activeAt: format(rul._watcher.getLastAlert().activeAt) - }] - : [] - const health = rul && rul._watcher ? rul._watcher.health : 'nodata' - const lastError = rul && rul._watcher ? rul._watcher.lastError : '' - const state = rul && rul._watcher ? rul._watcher.state || 'normal' : 'normal' - return { - name: rul.alert, - query: rul.expr, - duration: durationToMs(rul.for || '30s') / 1000, - alerts: alerts, - labels: rul.labels || {}, - annotations: rul.annotations || {}, - health: health, - lastError: lastError, - state: state, - type: 'alerting', - lastEvaluation: format(new Date()), - evaluationTime: 0.01 - } -} diff --git a/lib/handlers/alterts_data.js b/lib/handlers/alterts_data.js deleted file mode 100644 index d9319c35..00000000 --- a/lib/handlers/alterts_data.js +++ /dev/null @@ -1,30 +0,0 @@ -const axios = require('axios') -const logger = require('../logger') - -async function handler (req, res) { - req.body = JSON.parse(req.body) - if (!process.env.ALERTMAN_URL) { - return res.send('ok') - } - try { - logger.info(`POSTING \`${process.env.ALERTMAN_URL}/api/v2/alerts\` ${req.body.data.length}`) - await axios.post(`${process.env.ALERTMAN_URL}/api/v2/alerts`, - req.body.data.map(d => ({ - labels: { - ...JSON.parse(d.labels), - alertname: 'n1' - }, - annotations: { - string: d.string - }, - generatorURL: 'http://qryn/alerts' - })) - ) - } catch (err) { - logger.error(err, 'SEND ERROR') - throw err - } - return res.send('ok') -} - -module.exports = handler diff --git a/lib/handlers/common.js b/lib/handlers/common.js deleted file mode 100644 index 7d903d15..00000000 --- a/lib/handlers/common.js +++ /dev/null @@ -1,92 +0,0 @@ -const eng = require('../../plugins/engine') -const { parseCliQL } = require('../cliql') -const { Transform } = require('stream') -const { scanClickhouse, scanFingerprints } = require('../db/clickhouse') -const {EventEmitter} = require('events') - -module.exports.checkCustomPlugins = async (options) => { - options.API = options.API || { - logql: async (query, start, end, limit) => { - const params = { - query, - start, - end, - limit, - direction: 'backward', - step: '60s' - } - const req = { - query: params - } - const res = new Transform({ - transform (chunk, encoding, callback) { - callback(null, chunk) - } - }) - res.writeHead = () => {} - const cliqlParams = parseCliQL(req.query.query) - if (cliqlParams) { - scanClickhouse(cliqlParams, { res }, params) - } else { - await scanFingerprints( - req.query, - { res: res } - ) - } - let str = '' - res.on('data', (d) => { - str += d - }) - await new Promise((resolve, reject) => { - res.once('error', reject) - res.once('close', resolve) - res.once('end', resolve) - }) - return JSON.parse(str) - }/* , - promql: async () => { - - } */ - } - const plugins = eng.getPlg({ type: 'custom_processor' }) - for (const plugin of Object.values(plugins)) { - for (const e of Object.entries(options)) { - plugin[e[0]] = e[1] - } - if (plugin.check()) { - return await plugin.process() - } - } -} - -/** - * - * @param value {number} - * @returns {[number,boolean]} new value and if I should ingest it - */ -module.exports.checkNanValue = (value) => { - if (typeof value === 'number' && isNaN(value)) { - return [NaN, true] - } - return [value, true] -} - -const newResponse = () => { - let head = null - let onWriteHead = new EventEmitter() - return { - writeHead: (code, headers) => { - head = { - code, - headers - } - onWriteHead.emit('head', head) - }, - getHead: async () => { - if (head) { - return head - } - return await new Promise(resolve => onWriteHead.once('head', f)) - } - } -} diff --git a/lib/handlers/datadog_log_push.js b/lib/handlers/datadog_log_push.js deleted file mode 100644 index 3417a419..00000000 --- a/lib/handlers/datadog_log_push.js +++ /dev/null @@ -1,111 +0,0 @@ - -/* Datadig Log Indexing Handler - Accepts JSON formatted requests when the header Content-Type: application/json is sent. - - POST /api/v2/logs - - Accepts Datadog Log JSON Body objects: - - [{ - ddsource: "nginx", - ddtags: "env:staging,version:5.1", - hostname: "i-012345678", - message: "2019-11-19T14:37:58,995 INFO [process.name][20081] Hello World", - service: "payment", - }] - - -*/ - -const stringify = require('../utils').stringify -const DATABASE = require('../db/clickhouse') -const { bulk_labels, bulk, labels } = DATABASE.cache -const { fingerPrint } = require('../utils') -const { readonly, logType } = require('../../common') - -const tagsToObject = (data, delimiter = ',') => - Object.fromEntries(data.split(',').map(v => { - const fields = v.split(':') - return [fields[0], fields[1]] - })) - -async function handler (req, res) { - req.log.debug('Datadog Log Index Request') - if (!req.body) { - req.log.error('No Request Body or Target!') - return res.code(400).send('{"status":400, "error": { "reason": "No Request Body" } }') - } - if (readonly) { - req.log.error('Readonly! No push support.') - return res.code(400).send('{"status":400, "error": { "reason": "Read Only Mode" } }') - } - - let streams - if ( - req.headers['content-type'] && - req.headers['content-type'].indexOf('application/json') > -1 - ) { - // json body, handle as array - streams = req.body - } else { - // raw body, handle as ndjson - streams = req.body.split(/\n/) - } - req.log.info({ streams }, 'streams') - const promises = [] - if (streams) { - streams.forEach(function (stream) { - req.log.debug({ stream }, 'ingesting datadog log') - let finger = null - let JSONLabels = stream.ddtags ? tagsToObject(stream.ddtags) : {} - try { - try { - JSONLabels.type = 'datadog' - if (stream.ddsource || req.query.ddsource) JSONLabels.ddsource = stream.ddsource || req.query.ddsource - if (stream.source) JSONLabels.source = stream.source - if (stream.hostname) JSONLabels.hostname = stream.hostname - if (stream.source) JSONLabels.source = stream.source - // sort labels - JSONLabels = Object.fromEntries(Object.entries(JSONLabels).sort()) - } catch (err) { - req.log.error({ err }) - return - } - // Calculate Fingerprint - const strJson = stringify(JSONLabels) - finger = fingerPrint(strJson) - // Store Fingerprint - promises.push(bulk_labels.add([[ - new Date().toISOString().split('T')[0], - finger, - strJson, - JSONLabels.target || '', - logType - ]])) - for (const key in JSONLabels) { - req.log.debug({ key, data: JSONLabels[key] }, 'Storing label') - labels.add('_LABELS_', key) - labels.add(key, JSONLabels[key]) - } - } catch (err) { - req.log.error({ err }, 'failed ingesting datadog log') - } - - // Store Datadog Log - const values = [ - finger, - BigInt((new Date().getTime() * 1000) + '000'), - null, - stream.message, - logType - ] - req.log.debug({ finger, values }, 'store') - promises.push(bulk.add([values])) - }) - } - await Promise.all(promises) - // always 202 empty JSON - return res.code(202).send('{}') -} - -module.exports = handler diff --git a/lib/handlers/datadog_series_push.js b/lib/handlers/datadog_series_push.js deleted file mode 100644 index 2eae1421..00000000 --- a/lib/handlers/datadog_series_push.js +++ /dev/null @@ -1,125 +0,0 @@ -/* Datadig Series Indexing Handler - - Accepts JSON formatted requests when the header Content-Type: application/json is sent. - - { - "series": [ - { - "metric": "system.load.1", - "type": 0, - "points": [ - { - "timestamp": 1636629071, - "value": 0.7 - } - ], - "resources": [ - { - "name": "dummyhost", - "type": "host" - } - ] - } - ] - } - -*/ -const stringify = require('../utils').stringify -const DATABASE = require('../db/clickhouse') -const { bulk_labels, bulk, labels } = DATABASE.cache -const { fingerPrint } = require('../utils') -const { readonly, metricType } = require('../../common') - -async function handler (req, res) { - req.log.debug('Datadog Series Index Request') - if (!req.body) { - req.log.error('No Request Body!') - res.code(500).send() - return - } - if (readonly) { - req.log.error('Readonly! No push support.') - res.code(500).send() - return - } - let streams - if (req.headers['content-type'] && req.headers['content-type'].indexOf('application/json') > -1) { - streams = req.body.series - } - const promises = [] - if (streams) { - streams.forEach(function (stream) { - let JSONLabels = {} - let finger = null - try { - try { - for (const res of stream.resources) { - JSONLabels = { - ...JSONLabels, - ...res - } - } - JSONLabels.__name__ = stream.metric - } catch (err) { - req.log.error({ err }) - return - } - // Calculate Fingerprint - const strJson = stringify(JSONLabels) - finger = fingerPrint(strJson) - labels.add(finger.toString(), stream.labels) - // Store Fingerprint - promises.push(bulk_labels.add([[ - new Date().toISOString().split('T')[0], - finger, - strJson, - JSONLabels.__name__ || 'undefined', - metricType - ]])) - for (const key in JSONLabels) { - labels.add('_LABELS_', key) - labels.add(key, JSONLabels[key]) - } - } catch (err) { - req.log.error({ err }) - } - - if (stream.points) { - stream.points.forEach(function (entry) { - req.log.debug({ entry, finger }, 'BULK ROW') - if ( - !entry && - !entry.timestamp && - !entry.value - ) { - req.log.error({ entry }, 'no bulkable data') - return - } - const values = [ - finger, - BigInt(pad('0000000000000000000', entry.timestamp)), - entry.value, - JSONLabels.__name__ || 'undefined', - metricType - ] - promises.push(bulk.add([values])) - }) - } - }) - } - await Promise.all(promises) - res.code(202).send({ errors: [] }) -} - -function pad (pad, str, padLeft) { - if (typeof str === 'undefined') { - return pad - } - if (padLeft) { - return (pad + str).slice(-pad.length) - } else { - return (str + pad).substring(0, pad.length) - } -} - -module.exports = handler diff --git a/lib/handlers/default.js b/lib/handlers/default.js deleted file mode 100644 index 8a087dd8..00000000 --- a/lib/handlers/default.js +++ /dev/null @@ -1,6 +0,0 @@ -// Example Handler -function handler (req, res) { - return res.send(req) -}; - -module.exports = handler diff --git a/lib/handlers/echo.js b/lib/handlers/echo.js deleted file mode 100644 index 5b34cff2..00000000 --- a/lib/handlers/echo.js +++ /dev/null @@ -1,6 +0,0 @@ -// Echo Handler -function handler (req, res) { - return res.send('echo') -}; - -module.exports = handler diff --git a/lib/handlers/elastic_bulk.js b/lib/handlers/elastic_bulk.js deleted file mode 100644 index 3d3bc8f8..00000000 --- a/lib/handlers/elastic_bulk.js +++ /dev/null @@ -1,115 +0,0 @@ -/* Elastic Indexing Handler - Accepts JSON formatted requests when the header Content-Type: application/json is sent. - - POST /_bulk - POST /<target>/_bulk - -*/ - -const { asyncLogError, logType } = require('../../common') -const stringify = require('../utils').stringify -const DATABASE = require('../db/clickhouse') -const { bulk_labels, bulk, labels } = DATABASE.cache -const { fingerPrint } = require('../utils') -const { readonly } = require('../../common') - -async function handler (req, res) { - req.log.debug('ELASTIC Bulk Request') - if (!req.body) { - asyncLogError('No Request Body or Target!' + req.body, req.log) - return res.code(400).send('{"status":400, "error": { "reason": "No Request Body" } }') - } - if (readonly) { - asyncLogError('Readonly! No push support.', req.log) - return res.code(400).send('{"status":400, "error": { "reason": "Read Only Mode" } }') - } - - const docTarget = req.params.target || false - - let streams - if ( - req.headers['content-type'] && - req.headers['content-type'].indexOf('application/x-ndjson') > -1 - ) { - // ndjson body - streams = req.body.split(/\n/) - } else { - // assume ndjson raw body - streams = req.body.split(/\n/) - } - let lastTags = false - const promises = [] - if (streams) { - streams.forEach(function (stream) { - if (!stream) { - return - } - try { - stream = JSON.parse(stream) - } catch (err) { asyncLogError(err, req.log); return }; - - // Allow Index, Create. Discard Delete, Update. - if (stream.delete || stream.update) { - lastTags = false - return - } - var command = stream.index || stream.create || false; - if (command && !lastTags) { - lastTags = stream.index - return - } - - // Data Rows - let finger = null - let JSONLabels = lastTags - try { - try { - JSONLabels.type = 'elastic' - if (docTarget) JSONLabels._index = docTarget - JSONLabels = Object.fromEntries(Object.entries(JSONLabels).sort()) - } catch (err) { - asyncLogError(err, req.log) - return - } - // Calculate Fingerprint - const strJson = stringify(JSONLabels) - finger = fingerPrint(strJson) - req.log.debug({ JSONLabels, finger }, 'LABELS FINGERPRINT') - // Store Fingerprint - promises.push(bulk_labels.add([[ - new Date().toISOString().split('T')[0], - finger, - strJson, - JSONLabels.target || '', - logType - ]])) - for (const key in JSONLabels) { - req.log.debug({ key, data: JSONLabels[key] }, 'Storing label') - labels.add('_LABELS_', key) - labels.add(key, JSONLabels[key]) - } - } catch (err) { - asyncLogError(err, req.log) - } - - // Store Elastic Doc Object - const values = [ - finger, - BigInt((new Date().getTime() * 1000) + '000'), - null, - JSON.stringify(stream) || stream, - logType - ] - req.log.debug({ finger, values }, 'store') - promises.push(bulk.add([values])) - - // Reset State, Expect Command - lastTags = false - }) - } - await Promise.all(promises) - res.header('x-elastic-product', 'Elasticsearch') - return res.code(200).send('{"took":0, "errors": false }') -} - -module.exports = handler diff --git a/lib/handlers/elastic_index.js b/lib/handlers/elastic_index.js deleted file mode 100644 index 047d3a3d..00000000 --- a/lib/handlers/elastic_index.js +++ /dev/null @@ -1,104 +0,0 @@ - -/* Elastic Indexing Handler - Accepts JSON formatted requests when the header Content-Type: application/json is sent. - - POST /<target>/_doc/ - PUT /<target>/_doc/<_id> - PUT /<target>/_create/<_id> - POST /<target>/_create/<_id> - -*/ - -const { asyncLogError, logType } = require('../../common') -const stringify = require('../utils').stringify -const DATABASE = require('../db/clickhouse') -const { bulk_labels, bulk, labels } = DATABASE.cache -const { fingerPrint } = require('../utils') -const { readonly } = require('../../common') - - -async function handler (req, res) { - req.log.debug('ELASTIC Index Request') - if (!req.body || !req.params.target) { - asyncLogError('No Request Body or Target!', req.log) - return res.code(400).send('{"status":400, "error": { "reason": "No Request Body" } }') - } - if (readonly) { - asyncLogError('Readonly! No push support.', req.log) - return res.code(400).send('{"status":400, "error": { "reason": "Read Only Mode" } }') - } - - const docTarget = req.params.target || false - const docId = req.params.id || false - - let streams - if ( - req.headers['content-type'] && - req.headers['content-type'].indexOf('application/json') > -1 - ) { - // json body, handle as single node array - streams = [req.body] - } else { - // raw body, handle as ndjson - streams = req.body.split(/\n/) - } - req.log.info({ streams }, 'streams') - const promises = [] - if (streams) { - streams.forEach(function (stream) { - req.log.debug({ stream }, 'ingesting elastic doc') - let finger = null - let JSONLabels = {} - try { - try { - JSONLabels.type = 'elastic' - if (docTarget) JSONLabels._index = docTarget - if (docId) JSONLabels._id = docId - JSONLabels = Object.fromEntries(Object.entries(JSONLabels).sort()) - } catch (err) { - asyncLogError(err, req.log) - return - } - // Calculate Fingerprint - const strJson = stringify(JSONLabels) - finger = fingerPrint(strJson) - // Store Fingerprint - promises.push(bulk_labels.add([[ - new Date().toISOString().split('T')[0], - finger, - strJson, - JSONLabels.target || '', - logType - ]])) - for (const key in JSONLabels) { - req.log.debug({ key, data: JSONLabels[key] }, 'Storing label') - labels.add('_LABELS_', key) - labels.add(key, JSONLabels[key]) - } - } catch (err) { - asyncLogError(err, req.log) - } - // check if stream is JSON format - try { - stream = JSON.parse(stream) - } catch (err) { - asyncLogError(err, req.log) - }; - // Store Elastic Doc Object - const values = [ - finger, - BigInt((new Date().getTime() * 1000) + '000'), - null, - JSON.stringify(stream) || stream, - logType - ] - req.log.debug({ finger, values }, 'store') - promises.push(bulk.add([values])) - }) - } - await Promise.all(promises) - res.header('x-elastic-product', 'Elasticsearch') - return res.code(200).send('{"took":0, "errors": false }') -} - -module.exports = handler diff --git a/lib/handlers/errors.js b/lib/handlers/errors.js deleted file mode 100644 index 72be10db..00000000 --- a/lib/handlers/errors.js +++ /dev/null @@ -1,57 +0,0 @@ -const { asyncLogError } = require('../../common') - -class QrynError extends Error { - constructor (code, name, message) { - super(message) - this.code = code - this.name = name - } -} - -class QrynBadRequest extends QrynError { - constructor (message) { - super(400, 'Bad Request', message) - } -} - -class QrynNotFound extends QrynError { - constructor (message) { - super(404, 'Not Found', message) - } -} - -const handler = (err, req, res) => { - if (err instanceof QrynError) { - return res.status(err.code).send({ - statusCode: err.code, - error: err.name, - message: err.message - }) - } - if (err.name && err.name === 'FastifyError') { - return res.status(err.statusCode).send({ - statusCode: err.statusCode, - message: err.message - }) - } - if (err.name && err.name === 'UnauthorizedError') { - return res.status(401).send({ - statusCode: 401, - error: 'Unauthorized', - message: err.message - }) - } - asyncLogError(err, req.log) - return res.status(500).send({ - statusCode: 500, - error: 'Internal Server Error', - message: 'Internal Server Error' - }) -} - -module.exports = { - QrynError: QrynError, - QrynBadRequest: QrynBadRequest, - QrynNotFound: QrynNotFound, - handler -} diff --git a/lib/handlers/influx_health.js b/lib/handlers/influx_health.js deleted file mode 100644 index 36ddd8af..00000000 --- a/lib/handlers/influx_health.js +++ /dev/null @@ -1,13 +0,0 @@ -const { ping, ready } = require('../db/clickhouse') -async function handler (req, res) { - try { - if (!ready()) { - return res.send(new Error('qryn not ready')) - } - await ping() - return res.code(204).send('OK') - } catch (e) { - return res.send(new Error('qryn DB not ready')) - } -} -module.exports = handler diff --git a/lib/handlers/influx_write.js b/lib/handlers/influx_write.js deleted file mode 100644 index 48361946..00000000 --- a/lib/handlers/influx_write.js +++ /dev/null @@ -1,220 +0,0 @@ -/* Influx Line protocol Write Handler for Qryn */ -/* - Accepts Line protocols parsed by @qxip/influx-line-protocol-parser - - { - measurement: 'cpu_load_short', - timestamp: 1422568543702900257, - fields: [{ - value: 2 - }], - tags:[ - {direction: 'in'}, - {host: 'server01'}, - {region: 'us-west'}, - ] - } - - { - measurement:"syslog", - fields:[ - {facility_code: 14}, - {message: "warning message here"}, - {severity_code: 4}, - {procid: "12345"}, - {timestamp: 1534418426076077000}, - {version: 1} - ], - tags:[ - {appname: "myapp"}, - {facility: "console"}, - {host: "myhost"}, - {hostname: "myhost"}, - {severity: "warning"} - ] - } - -*/ - -const stringify = require('../utils').stringify -const influxParser = require('../influx') -const { asyncLogError, errors, bothType, logType, metricType } = require('../../common') -const DATABASE = require('../db/clickhouse') -const { bulk_labels, bulk, labels } = DATABASE.cache -const { fingerPrint } = require('../utils') -const { readonly } = require('../../common') -const { checkNanValue } = require('./common') - -async function handler (req, res) { - if (!req.body && !req.body.metrics) { - asyncLogError('No Request Body!', req.log) - return - } - if (readonly) { - asyncLogError('Readonly! No push support.', req.log) - return res.code(500).send('') - } - await influxParser.init() - let streams = null - try { - streams = influxParser.parse(req.body) - } catch (e) { - throw new errors.QrynBadRequest(e.toString()) - } - const promises = [] - if (process.env.ADVANCED_TELEGRAF_METRICS_SCHEMA === 'telegraf-prometheus-v2') { - await Promise.all(telegrafPrometheusV1(streams)) - } else if (streams) { - streams.forEach(function (stream) { - let JSONLabels = {} - let JSONFields = {} - let finger = null - let strLabels = '' - try { - if (stream.tags) { - JSONLabels = stream.tags - } - if (stream.fields) { - JSONFields = stream.fields - } - if (stream.measurement && stream.measurement !== 'syslog' && !JSONFields.message) { - JSONLabels.__name__ = stream.measurement || 'null' - } - // Calculate Fingerprint - strLabels = stringify(Object.fromEntries(Object.entries(JSONLabels).sort())) - finger = fingerPrint(strLabels) - labels.add(finger.toString(), stream.labels) - // Store Fingerprint - for (const key in JSONLabels) { - // req.log.debug({ key, data: JSONLabels[key] }, 'Storing label'); - labels.add('_LABELS_', key) - labels.add(key, JSONLabels[key]) - } - } catch (err) { - asyncLogError(err, req.log) - } - let type = bothType - const timestamp = stream.timestamp || JSONFields.timestamp - /* metrics */ - if (stream.fields && stream.measurement !== 'syslog' && !JSONFields.message) { - for (const [key, value] of Object.entries(JSONFields)) { - // req.log.debug({ key, value, finger }, 'BULK ROW'); - if ( - !key && - !timestamp && - !value - ) { - asyncLogError('no bulkable data', req.log) - return res.code(204).send('') - } - const [_value, ingest] = checkNanValue(value) - if (!ingest) { - return - } - const values = [ - finger, - BigInt(pad('0000000000000000000', timestamp, true)), - parseFloat(_value) || 0, - key || '' - ] - bulk.add([values]) - } - type = metricType - /* logs or syslog */ - } else if (stream.measurement === 'syslog' || JSONFields.message) { - // Send fields as a JSON object for qryn to parse - // const message = JSON.stringify(JSONFields) - const values = [ - finger, - BigInt(pad('0000000000000000000', timestamp)), - null, - JSONFields.message - ] - bulk.add([values]) - type = logType - } - - bulk_labels.add([[ - new Date().toISOString().split('T')[0], - finger, - strLabels, - stream.measurement || '', - type - ]]) - }) - } - await Promise.all(promises) - return res.code(204).send('') -} - -function telegrafPrometheusV1 (stream) { - const promises = [] - for (const entry of stream) { - const timestamp = BigInt(entry.timestamp) - if (entry.measurement === 'syslog' || entry.fields.message) { - const labels = { - ...entry.tags, - measurement: entry.measurement - } - const strLabels = stringify(Object.fromEntries(Object.entries(labels).sort())) - const fp = fingerPrint(strLabels) - promises.push(bulk_labels.add([[ - new Date().toISOString().split('T')[0], - fp, - strLabels, - entry.measurement || '', - logType - ]])) - const values = [ - fp, - timestamp, - 0, - entry.fields.message || '', - logType - ] - promises.push(bulk.add([values])) - } - for (const [key, value] of Object.entries(entry.fields)) { - const iValue = parseFloat(value) - if (typeof iValue !== 'number') { - continue - } - const labels = { - ...entry.tags, - measurement: entry.measurement, - __name__: key - } - const strLabels = stringify(Object.fromEntries(Object.entries(labels).sort())) - const fp = fingerPrint(strLabels) - promises.push(bulk_labels.add([[ - new Date().toISOString().split('T')[0], - fp, - strLabels, - entry.measurement || '', - metricType - ]])) - const values = [ - fp, - timestamp, - iValue || 0, - key || '', - metricType - ] - promises.push(bulk.add([values])) - } - } - return promises -} - -function pad (pad, str, padLeft) { - if (typeof str === 'undefined') { - return pad - } - if (padLeft) { - return (pad + str).slice(-pad.length) - } else { - return (str + pad).substring(0, pad.length) - } -} - -module.exports = handler diff --git a/lib/handlers/label.js b/lib/handlers/label.js deleted file mode 100644 index 6064263f..00000000 --- a/lib/handlers/label.js +++ /dev/null @@ -1,35 +0,0 @@ -/* Label Handler */ -/* - For retrieving the names of the labels one can query on. - Responses looks like this: -{ - "values": [ - "instance", - "job", - ... - ] -} -*/ - -const clickhouse = require('../db/clickhouse') -const utils = require('../utils') -const { clusterName, bothType, logType } = require('../../common') -const dist = clusterName ? '_dist' : '' - -async function handler (req, res) { - req.log.debug('GET /loki/api/v1/label') - const types = req.types || [bothType, logType] - let where = [ - req.query.start && !isNaN(parseInt(req.query.start)) ? `date >= toDate(FROM_UNIXTIME(intDiv(${parseInt(req.query.start)}, 1000000000)))` : null, - req.query.end && !isNaN(parseInt(req.query.end)) ? `date <= toDate(FROM_UNIXTIME(intDiv(${parseInt(req.query.end)}, 1000000000)))` : null, - `type IN (${types.map(t => `${t}`).join(',')})` - ].filter(w => w) - where = where.length ? `WHERE ${where.join(' AND ')}` : '' - const q = `SELECT DISTINCT key FROM time_series_gin${dist} ${where} FORMAT JSON` - console.log(q) - const allLabels = await clickhouse.rawRequest(q, null, utils.DATABASE_NAME()) - const resp = { status: 'success', data: allLabels.data.data.map(r => r.key) } - return res.send(resp) -} - -module.exports = handler diff --git a/lib/handlers/label_values.js b/lib/handlers/label_values.js deleted file mode 100644 index 885b66a1..00000000 --- a/lib/handlers/label_values.js +++ /dev/null @@ -1,40 +0,0 @@ -/* Label Value Handler */ -/* - For retrieving the label values one can query on. - Responses looks like this: - { - "values": [ - "default", - "cortex-ops", - ... - ] -} -*/ - -const clickhouse = require('../db/clickhouse') -const Sql = require('@cloki/clickhouse-sql') -const utils = require('../utils') -const { clusterName, bothType, logType } = require('../../common') -const dist = clusterName ? '_dist' : '' - -async function handler (req, res) { - req.log.debug(`GET /api/prom/label/${req.params.name}/values`) - const types = req.types || [bothType, logType] - let where = [ - `key = ${Sql.val(req.params.name)}`, - req.query.start && !isNaN(parseInt(req.query.start)) ? `date >= toDate(FROM_UNIXTIME(intDiv(${parseInt(req.query.start)}, 1000000000)))` : null, - req.query.end && !isNaN(parseInt(req.query.end)) ? `date <= toDate(FROM_UNIXTIME(intDiv(${parseInt(req.query.end)}, 1000000000)))` : null, - `type IN (${types.map(t => `${t}`).join(',')})` - ].filter(w => w) - where = `WHERE ${where.join(' AND ')}` - let limit = '' - if (process.env.ADVANCED_SERIES_REQUEST_LIMIT) { - limit = `LIMIT ${process.env.ADVANCED_SERIES_REQUEST_LIMIT}` - } - const q = `SELECT DISTINCT val FROM time_series_gin${dist} ${where} ${limit} FORMAT JSON` - const allValues = await clickhouse.rawRequest(q, null, utils.DATABASE_NAME()) - const resp = { status: 'success', data: allValues.data.data.map(r => r.val) } - return res.send(resp) -} - -module.exports = handler diff --git a/lib/handlers/newrelic_log_push.js b/lib/handlers/newrelic_log_push.js deleted file mode 100644 index 1396be24..00000000 --- a/lib/handlers/newrelic_log_push.js +++ /dev/null @@ -1,127 +0,0 @@ -/* New Relic Log Ingestor (https://docs.newrelic.com/docs/logs/log-api/introduction-log-api/) - - Accepts JSON formatted requests when the header Content-Type: application/json is sent. - Example of the JSON format: - - POST /log/v1 HTTP/1.1 - Host: log-api.newrelic.com - Content-Type: application/json - Api-Key: <YOUR_LICENSE_KEY> - Content-Length: 319 - [{ - "common": { - "attributes": { - "logtype": "accesslogs", - "service": "login-service", - "hostname": "login.example.com" - } - }, - "logs": [{ - "timestamp": <TIMESTAMP_IN_UNIX_EPOCH><, - "message": "User 'xyz' logged in" - },{ - "timestamp": <TIMESTAMP_IN_UNIX_EPOCH, - "message": "User 'xyz' logged out", - "attributes": { - "auditId": 123 - } - }] - }] -*/ - -const { QrynBadRequest } = require('./errors') -const stringify = require('../utils').stringify -const DATABASE = require('../db/clickhouse') -const { bulk_labels, bulk, labels } = DATABASE.cache -const { fingerPrint } = require('../utils') -const { readonly, logType } = require('../../common') - -async function handler (req, res) { - req.log.debug('NewRelic Log Index Request') - if (!req.body) { - req.log.error('No Request Body') - throw new QrynBadRequest('No request body') - } - if (readonly) { - req.log.error('Readonly! No push support.') - throw new QrynBadRequest('Read only mode') - } - let streams - if (Array.isArray(req.body)) { - // Bulk Logs - streams = req.body - } else { - // Single Log - const tags = req.body - const { timestamp, message } = tags - if (!timestamp) { - throw new QrynBadRequest('Log timestamp is undefined') - } - if (!message) { - throw new QrynBadRequest('Log message is undefined') - } - delete tags.message - delete tags.timestamp - streams = [{ - common: { attributes: tags }, - logs: [{ timestamp, message }] - }] - } - req.log.info({ streams }, 'streams') - const promises = [] - if (streams) { - streams.forEach(function (stream) { - req.log.debug({ stream }, 'ingesting newrelic log') - let finger = null - let JSONLabels = stream?.common?.attributes || stream?.attributes || {} - try { - JSONLabels.type = 'newrelic' - JSONLabels = Object.fromEntries(Object.entries(JSONLabels).sort()) - - // Calculate Fingerprint - const strJson = stringify(JSONLabels) - finger = fingerPrint(strJson) - // Store Fingerprint - for (const key in JSONLabels) { - req.log.debug({ key, data: JSONLabels[key] }, 'Storing label') - labels.add('_LABELS_', key) - labels.add(key, JSONLabels[key]) - } - - const dates = {} - // Queue Array logs - if (stream.logs) { - stream.logs.forEach(function (log) { - const ts = BigInt(`${log.timestamp}0000000000000000000`.substring(0, 19)) - dates[new Date(parseInt((ts / BigInt(1000000)).toString())).toISOString().split('T')[0]] = 1 - // Store NewRelic Log - // TODO: handle additional attributes! - const values = [ - finger, - ts, - null, - log.message, - logType - ] - promises.push(bulk.add([values])) - }) - } - for (const d of Object.keys(dates)) { - promises.push(bulk_labels.add([[ - d, - finger, - strJson, - JSONLabels.target || '', - logType - ]])) - } - } catch (err) { - req.log.error({ err }, 'failed ingesting datadog log') - } - }) - } - await Promise.all(promises) - return res.code(200).send('OK') -} - -module.exports = handler diff --git a/lib/handlers/otlp_log_push.js b/lib/handlers/otlp_log_push.js deleted file mode 100644 index 14f7edfa..00000000 --- a/lib/handlers/otlp_log_push.js +++ /dev/null @@ -1,110 +0,0 @@ -const DATABASE = require('../db/clickhouse') -const { asyncLogError, logType, metricType, bothType, readonly } = require('../../common') -const UTILS = require('../utils') -const stringify = UTILS.stringify -const fingerPrint = UTILS.fingerPrint -const { bulk_labels, bulk, labels } = DATABASE.cache - -async function handle (req, res) { - if (readonly) { - asyncLogError('Readonly! No push support.', req.log) - return res.code(500).send() - } - try { - const promises = [] - const fingerprints = {} - for (const resourceLogsEntry of req.body.resourceLogs) { - const resAttrs = resource2Attrs(resourceLogsEntry.resource) - for (const scopeLogsEntry of resourceLogsEntry.scopeLogs) { - const scopeAttrs = { - ...resAttrs, - ...resource2Attrs(scopeLogsEntry.scope) - } - for (const logRecord of scopeLogsEntry.logRecords) { - const logAttrs = { - ...scopeAttrs, - ...resource2Attrs(logRecord) - } - if (logRecord.severityText) { - logAttrs.level = logRecord.severityText - } - const labels = stringify(logAttrs) - const fingerprint = fingerPrint(labels) - const ts = logRecord.timeUnixNano - ? BigInt(logRecord.timeUnixNano) - : logRecord.observedTimeUnixNano - ? BigInt(logRecord.observedTimeUnixNano) - : BigInt(Date.now()) * BigInt(1000000) - promises.push(bulk.add([[ - fingerprint, - ts, - null, - anyValueToString(logRecord.body), - logType - ]])) - const date = new Date(Number(ts / BigInt(1000000))).toISOString().split('T')[0] - !fingerprints[fingerprint] && promises.push(bulk_labels.add([[ - date, - fingerprint, - labels, - labels.name || '', - logType - ]])) - fingerprints[fingerprint] = true - } - } - } - await Promise.all(promises) - } catch (error) { - await asyncLogError(error, req.log) - res.status(500).send({ error: 'Internal Server Error' }) - } -} - -function resource2Attrs (resource) { - if (!resource || !resource.attributes) { - return {} - } - const attrs = {} - for (const attribute of resource.attributes) { - attrs[normalizeAttrName(attribute.key)] = anyValueToString(attribute.value) - } - return attrs -} - -function normalizeAttrName (name) { - return name.replaceAll(/[^a-zA-Z0-9_]/g, '_') -} - -function anyValueToString (value) { - if (!value) { - return '' - } - if (value.stringValue) { - return value.stringValue - } - if (value.boolValue) { - return value.boolValue ? 'true' : 'false' - } - if (value.intValue) { - return value.intValue.toString() - } - if (value.doubleValue) { - return value.doubleValue.toString() - } - if (value.bytesValue) { - return Buffer.from(value.bytesValue).toString('base64') - } - if (value.arrayValue) { - return JSON.stringify(value.arrayValue.values.map(anyValueToString)) - } - if (value.kvlistValue) { - return JSON.stringify(value.kvlistValue.values.reduce((agg, pair) => ({ - ...agg, - [pair.key]: anyValueToString(pair.value) - }))) - } - return '' -} - -module.exports = handle diff --git a/lib/handlers/otlp_push.js b/lib/handlers/otlp_push.js deleted file mode 100644 index 73a62d1e..00000000 --- a/lib/handlers/otlp_push.js +++ /dev/null @@ -1,53 +0,0 @@ -/* Zipkin Push Handler - Accepts JSON formatted requests when the header Content-Type: application/json is sent. - Example of the Zipkin span JSON format: - [{ - "id": "1234", - "traceId": "0123456789abcdef", - "timestamp": 1608239395286533, - "duration": 100000, - "name": "span from bash!", - "tags": { - "http.method": "GET", - "http.path": "/api" - }, - "localEndpoint": { - "serviceName": "shell script" - } - }] -*/ - -const { asyncLogError } = require('../../common') -const { pushOTLP } = require('../db/clickhouse') -const { readonly } = require('../../common') - -async function handler (req, res) { - req.log.debug('POST /tempo/api/push') - if (!req.body) { - asyncLogError('No Request Body!', req.log) - return res.code(500).send() - } - if (readonly) { - asyncLogError('Readonly! No push support.', req.log) - return res.code(500).send() - } - const streams = req.body - const spans = [] - for (const res of streams.resourceSpans) { - const resAttrs = res.resource && res.resource.attributes ? res.resource.attributes : [] - for (const scope of res.scopeSpans) { - scope.spans = scope.spans.map(span => ({ - ...span, - attributes: [ - ...(span.attributes ? span.attributes: []), - ...resAttrs - ] - })) - spans.push.apply(spans, scope.spans) - } - } - await pushOTLP(spans) - return res.code(200).send('OK') -} - -module.exports = handler diff --git a/lib/handlers/prom_default.js b/lib/handlers/prom_default.js deleted file mode 100644 index 40279fcd..00000000 --- a/lib/handlers/prom_default.js +++ /dev/null @@ -1,42 +0,0 @@ -/* Emulated PromQL Query Handler */ - -async function buildinfo (req, res) { - const path = req.url - req.log.debug('PROM Handler', path) - return res.send( - { - status: 'success', - data: { - version: '2.13.1', - revision: 'cb7cbad5f9a2823a622aaa668833ca04f50a0ea7', - branch: 'master', - buildUser: 'qryn@qxip', - buildDate: '29990401-13:37:420', - goVersion: 'go1.18.1' - } - }) -} - -async function rules (req, res) { - req.log.debug('PROM Handler', req.url) - return res.send({ - status: 'success', - data: { - groups: [ - { - rules: [] - } - ] - } - }) -} - -async function misc (req, res) { - return res.send({ status: 'success', data: {} }) -} - -module.exports = { - buildinfo, - rules, - misc -} diff --git a/lib/handlers/prom_push.js b/lib/handlers/prom_push.js deleted file mode 100644 index a6de10df..00000000 --- a/lib/handlers/prom_push.js +++ /dev/null @@ -1,105 +0,0 @@ -/* Prometheus Remote Write Handler for Qryn */ -/* - - Accepts Prometheus WriteRequest Protobuf events - - { "timeseries":[ - { - "labels":[{"name":"test","response_code":"200"}], - "samples":[{"value":7.1,"timestamp":"1641758471000"}] - }] - } - -*/ -const { asyncLogError, metricType } = require('../../common') -const stringify = require('../utils').stringify -const DATABASE = require('../db/clickhouse') -const { bulk_labels, bulk, labels } = DATABASE.cache -const { fingerPrint } = require('../utils') -const { readonly } = require('../../common') -const { checkNanValue } = require('./common') - -async function handler (req, res) { - const self = this - req.log.debug('POST /api/v1/prom/remote/write') - if (!req.body) { - asyncLogError('No Request Body!', req.log) - return res.code(500).send() - } - if (readonly) { - asyncLogError('Readonly! No push support.', req.log) - return res.code(500).send() - } - let streams - if (req.headers['content-type'] && req.headers['content-type'].indexOf('application/x-protobuf') > -1) { - streams = req.body.timeseries - } - const promises = [] - if (streams) { - streams.forEach(function (stream) { - let JSONLabels - let finger = null - try { - JSONLabels = stream.labels.reduce((sum, l) => { - sum[l.name] = l.value - return sum - }, {}) - // Calculate Fingerprint - const strJson = stringify(JSONLabels) - finger = fingerPrint(strJson) - labels.add(finger.toString(), stream.labels) - - const dates = {} - if (stream.samples) { - stream.samples.forEach(function (entry) { - if ( - !entry && - !entry.timestamp && - !entry.value - ) { - asyncLogError({ entry }, req.log) - return - } - const [value, ingest] = checkNanValue(entry.value) - if (!ingest) { - return - } - const ts = BigInt(entry.timestamp) - const values = [ - finger, - ts, - value, - JSONLabels.__name__ || 'undefined', - metricType - ] - dates[ - new Date(parseInt((ts / BigInt('1000000')).toString())).toISOString().split('T')[0] - ] = 1 - promises.push(bulk.add([values])) - }) - } - for (const d of Object.keys(dates)) { - // Store Fingerprint - promises.push(bulk_labels.add([[ - d, - finger, - strJson, - JSONLabels.__name__ || 'undefined', - metricType - ]])) - for (const key in JSONLabels) { - labels.add('_LABELS_', key) - labels.add(key, JSONLabels[key]) - } - } - } catch (err) { - asyncLogError(err, req.log) - return res.code(500).send() - } - }) - } - await Promise.all(promises) - return res.code(204).send() -} - -module.exports = handler diff --git a/lib/handlers/prom_query.js b/lib/handlers/prom_query.js deleted file mode 100644 index fa03d8b6..00000000 --- a/lib/handlers/prom_query.js +++ /dev/null @@ -1,46 +0,0 @@ -/* Emulated PromQL Query Handler */ - -const { asyncLogError, CORS } = require('../../common') -const { instantQuery } = require('../../promql') -const empty = '{"status" : "success", "data" : {"resultType" : "scalar", "result" : []}}'; // to be removed -const test = () => `{"status" : "success", "data" : {"resultType" : "scalar", "result" : [${Math.floor(Date.now() / 1000)}, "2"]}}`; // to be removed -const exec = (val) => `{"status" : "success", "data" : {"resultType" : "scalar", "result" : [${Math.floor(Date.now() / 1000)}, val]}}`; // to be removed - -async function handler (req, res) { - req.log.debug('GET /loki/api/v1/query') - const resp = { - status: "success", - data: { - resultType: "vector", - result: [] - } - } - if (req.method === 'POST') { - req.query = req.body - } - if (!req.query.query) { - return res.send(resp) - } - if (req.query.query === '1+1') { - return res.code(200).send(test()) - } else if (Number(req.query.query)) { - return res.code(200).send(exec(Number(req.query.query))) - } - /* remove newlines */ - req.query.query = req.query.query.replace(/\n/g, ' ') - req.query.time = req.query.time ? Number(req.query.time) * 1000 : Date.now() - /* transpile to logql */ - try { - const response = await instantQuery(req.query.query, req.query.time) - return res.code(200) - .headers({ - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin': CORS - }).send(response) - } catch (err) { - asyncLogError(err, req.log) - return res.code(500).send(JSON.stringify({ status: 'error', error: err.message })) - } -} - -module.exports = handler diff --git a/lib/handlers/prom_query_range.js b/lib/handlers/prom_query_range.js deleted file mode 100644 index 2992c4a2..00000000 --- a/lib/handlers/prom_query_range.js +++ /dev/null @@ -1,31 +0,0 @@ -/* Emulated PromQL Query Handler */ -/* - Converts PromQL to LogQL queries, accepts the following parameters in the query-string: - query: a PromQL query - limit: max number of entries to return - start: the start time for the query, as a nanosecond Unix epoch (nanoseconds since 1970) - end: the end time for the query, as a nanosecond Unix epoch (nanoseconds since 1970) - direction: forward or backward, useful when specifying a limit - regexp: a regex to filter the returned results, will eventually be rolled into the query language -*/ - -const { rangeQuery } = require('../../promql/index') - -async function handler (req, res) { - req.log.debug('GET /api/v1/query_range') - const request = req.method === 'POST' ? req.body : req.query - const startMs = parseInt(request.start) * 1000 || Date.now() - 60000 - const endMs = parseInt(request.end) * 1000 || Date.now() - const stepMs = parseInt(request.step) * 1000 || 15000 - const query = request.query - try { - const result = await rangeQuery(query, startMs, endMs, stepMs) - return res.code(200).send(result) - } catch (err) { - console.log(req.query) - console.log(err) - return res.code(500).send(JSON.stringify({ status: 'error', error: err.message })) - } -} - -module.exports = handler diff --git a/lib/handlers/prom_series.js b/lib/handlers/prom_series.js deleted file mode 100644 index c3abd3e8..00000000 --- a/lib/handlers/prom_series.js +++ /dev/null @@ -1,40 +0,0 @@ -const { scanSeries } = require('../db/clickhouse') -const { CORS } = require('../../common') -const { QrynError } = require('./errors') -const {series} = require('../../promql/index') - -// Series Handler -async function handler (req, res) { - if (req.method === 'POST') { - req.query = req.body - } - let query = req.query.match || req.query['match[]'] - // bypass queries unhandled by transpiler - if (query.includes('node_info')) { - return res.send({ status: 'success', data: [] }) - } - if (!Array.isArray(query)) { - query = [query] - } - const startMs = req.query.start ? parseInt(req.query.start) * 1000 : Date.now() - 7 * 24 * 3600 * 1000 - const endMs = req.query.end ? parseInt(req.query.end) * 1000 : Date.now() - 7 * 24 * 3600 * 1000 - const result = [] - try { - query = query.map(async (m) => { - const _result = await series(m, startMs, endMs) - result.push.apply(result, _result) - }) - await Promise.all(query) - return res.code(200).headers({ - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin': CORS - }).send(JSON.stringify({ - status: 'success', - data: result - })) - } catch (err) { - return res.code(500).send(JSON.stringify({ status: 'error', error: err.message })) - } -} - -module.exports = handler diff --git a/lib/handlers/promlabel.js b/lib/handlers/promlabel.js deleted file mode 100644 index 6c7bf842..00000000 --- a/lib/handlers/promlabel.js +++ /dev/null @@ -1,28 +0,0 @@ -/* Label Handler */ -/* - For retrieving the names of the labels one can query on. - Responses looks like this: -{ - "values": [ - "instance", - "job", - ... - ] -} -*/ - -const { bothType, metricType } = require('../../common') - -async function handler (req, res) { - await require('./label.js')({ - ...req, - types: [bothType, metricType], - query: { - ...req.query, - start: req.query.start ? parseInt(req.query.start) * 1e9 : undefined, - end: req.query.end ? parseInt(req.query.end) * 1e9 : undefined - } - }, res) -} - -module.exports = handler diff --git a/lib/handlers/promlabel_values.js b/lib/handlers/promlabel_values.js deleted file mode 100644 index 14db8a48..00000000 --- a/lib/handlers/promlabel_values.js +++ /dev/null @@ -1,28 +0,0 @@ -/* Label Value Handler */ -/* - For retrieving the label values one can query on. - Responses looks like this: - { - "values": [ - "default", - "cortex-ops", - ... - ] -} -*/ - -const { bothType, metricType } = require('../../common') - -async function handler (req, res) { - await require('./label_values.js')({ - ...req, - types: [bothType, metricType], - query: { - ...req.query, - start: req.query.start ? parseInt(req.query.start) * 1e9 : undefined, - end: req.query.end ? parseInt(req.query.end) * 1e9 : undefined - } - }, res) -} - -module.exports = handler diff --git a/lib/handlers/push.js b/lib/handlers/push.js deleted file mode 100644 index bc5ea0b7..00000000 --- a/lib/handlers/push.js +++ /dev/null @@ -1,288 +0,0 @@ -/* Push Handler */ -/* - Accepts JSON formatted requests when the header Content-Type: application/json is sent. - Example of the JSON format: - { - "streams": [ - { - "labels": "{foo=\"bar\"}", - "entries": [ - {"ts": "2018-12-18T08:28:06.801064-04:00", "line": "baz"} - ] - } - ] - } -*/ - -const { chain } = require('stream-chain') -const { parser } = require('stream-json') -const { Transform } = require('stream') -const FilterBase = require('stream-json/filters/FilterBase') -const StreamValues = require('stream-json/streamers/StreamValues') -const logger = require('../logger') -const UTILS = require('../utils') -const DATABASE = require('../db/clickhouse') -const { asyncLogError, logType, metricType, bothType } = require('../../common') -const stringify = UTILS.stringify -const fingerPrint = UTILS.fingerPrint -const { bulk_labels, bulk, labels } = DATABASE.cache -const toJson = UTILS.toJSON -const { readonly } = require('../../common') - -function processStream (stream, labels, bulkLabels, bulk, toJSON, fingerPrint) { - let finger = null - let JSONLabels - const promises = [] - if (stream.stream) { - JSONLabels = stream.stream - } else { - JSONLabels = toJSON( - stream.labels //stream.labels.replace(/\!?="/g, ':"') - ) - } - // Calculate Fingerprint - const strJson = stringify(JSONLabels) - let type = 3 - finger = fingerPrint(strJson) - labels.add(finger.toString(), finger.toString()) - for (const key in JSONLabels) { - labels.add('_LABELS_', key) - labels.add(key, JSONLabels[key]) - } - const dates = {} - if (stream.entries) { - const values = [] - stream.entries.forEach(function (entry) { - if ( - !entry && - (!entry.timestamp || !entry.ts) && - (!entry.value || !entry.line) - ) { - console.error('no bulkable data', entry) - return - } - if (!entry.value) { - type &= logType - } - if (!entry.line || entry.line === '') { - type &= metricType - } - const ts = UTILS.parseStringifiedNanosOrRFC3339(entry.timestamp || entry.ts) - values.push([ - finger, - ts, - (typeof entry.value !== 'number') ? 0 : entry.value, - entry.line || '', - type === 3 ? bothType : type - ]) - dates[new Date(Number(ts / BigInt(1000000))).toISOString().split('T')[0]] = true - }) - promises.push(bulk.add(values)) - } - if (stream.values) { - const values = [] - stream.values.forEach(function (value) { - if (!value && !value[0] && !value[1]) { - console.error('no bulkable data', value) - return - } - - if (typeof value[2] === 'undefined') { - type &= logType - } - if (!value[1]) { - type &= metricType - } - - const ts = BigInt(value[0]) - values.push([ - finger, - BigInt(value[0]), - (typeof value[2] !== 'number') ? 0 : value[2], - value[1] || '', - type === 3 ? bothType : type - ]) - dates[new Date(Number(ts / BigInt(1000000))).toISOString().split('T')[0]] = true - }) - promises.push(bulk.add(values)) - } - for (const date of Object.keys(dates)) { - // Store Fingerprint - promises.push(bulkLabels.add([[ - date, - finger, - strJson, - JSONLabels.name || '', - type === 3 ? bothType : type - ]])) - } - return Promise.all(promises) -} - -async function handler (req, res) { - req.log.debug('POST /loki/api/v1/push') - if (!req.body) { - await processRawPush(req, DATABASE.cache.labels, bulk_labels, bulk, - toJson, fingerPrint) - return res.code(200).send() - } - if (readonly) { - asyncLogError('Readonly! No push support.', req.log) - return res.code(500).send() - } - let streams - if ( - req.headers['content-type'] && - req.headers['content-type'].indexOf('application/json') > -1 - ) { - streams = req.body.streams - } else if ( - req.headers['content-type'] && - req.headers['content-type'].indexOf('application/x-protobuf') > -1 - ) { - // streams = messages.PushRequest.decode(req.body) - streams = req.body - } - const promises = [] - if (streams) { - streams.forEach(function (stream) { - promises.push(processStream(stream, - DATABASE.cache.labels, DATABASE.cache.bulk_labels, DATABASE.cache.bulk, - UTILS.toJSON, fingerPrint)) - }) - await Promise.all(promises) - } - return res.code(204).send() -} - -class StackChecker extends FilterBase { - _checkChunk (chunk) { - /**/ - return false - } - - _check (chunk, _, callback) { - const self = this - return super._check(chunk, _, () => { - this.push({ - ...chunk, - stack: self._stack.filter(s => s !== null && typeof s !== 'undefined').join('.') - }) - callback(null) - }) - } -} - -class ConditionalStreamValues extends StreamValues { - constructor (options) { - super() - this.filter = options.filter - this.id = options.id - this.syncPasses = 0 - } - - __transform (chunk, encoding, callback) { - if (!chunk) { - this.pass(chunk, encoding) - callback(null) - return - } - if (chunk.isProcessed) { - this.pass(chunk, encoding) - callback(null) - return - } - this.stack = chunk.stack - if (!this.filter.test(chunk.stack)) { - this.pass(chunk, encoding) - callback(null) - return - } - super._transform(chunk, encoding, callback) - } - - _transform (chunk, encoding, callback) { - this.__transform(chunk, encoding, callback) - } - - pass (chunk, encoding) { - return super.push(chunk, encoding) - } - - push (chunk, encoding) { - if (!chunk) { - return super.push(chunk, encoding) - } - if (!chunk.value) { - return - } - return super.push({ - ...chunk, - stack: this.stack, - isProcessed: this.id - }, encoding) - } -} - -const processRawPush = async (req, labels, bulkLabels, bulkValues, toJSON, fingerPrint) => { - let stream = null - const promises = [] - const addPromise = () => { - if (stream && (stream.values || stream.entries)) { - const idx = promises.length - promises.push(processStream({ ...stream }, labels, bulkLabels, bulkValues, toJSON, fingerPrint) - .then(() => { promises[idx] = null }, (err) => { promises[idx] = err })) - stream = { ...stream, values: [] } - } - } - const pipeline = chain([ - req.raw, - parser(), - new StackChecker(), - new Transform({ - objectMode: true, - transform: function (chunk, encoding, callback) { - if (chunk && chunk.name === 'startObject' && - /^streams\.\d+$/.test(chunk.stack)) { - addPromise() - stream = {} - } - callback(null, chunk) - } - }), - new ConditionalStreamValues({ filter: /^streams\.\d+\.stream/, id: 'stream' }), - new ConditionalStreamValues({ filter: /^streams\.\d+\.values\.\d+/, id: 'values' }) - ]) - let size = 0 - pipeline.on('data', data => { - switch (data.isProcessed) { - case 'stream': - stream = { stream: data.value } - break - case 'values': - if (!stream) { - throw new Error('labels undefined') - } - stream.values = stream.values || [] - stream.values.push(data.value) - size += data.value[0].toString().length + - data.value[1].toString().length + - (data.value[2] ? data.value[2].toString().length : 0) - if (size > 100000) { - addPromise() - size = 0 - } - } - }) - await new Promise((resolve, reject) => { - pipeline.once('end', resolve) - pipeline.once('error', reject) - }) - const err = promises.find(p => p instanceof Error) - if (err) { - throw err - } - await Promise.all(promises.filter(p => p)) -} - -module.exports = handler diff --git a/lib/handlers/query.js b/lib/handlers/query.js deleted file mode 100644 index 6b6376db..00000000 --- a/lib/handlers/query.js +++ /dev/null @@ -1,43 +0,0 @@ -// Query Handler -const { asyncLogError, CORS } = require('../../common') -const { instantQueryScan } = require('../db/clickhouse') - -async function handler (req, res) { - req.log.debug('GET /loki/api/v1/query') - const resp = { streams: [] } - if (!req.query.query) { - return res.send(resp) - } - console.log(req.query.query) - const m = req.query.query.match(/^vector *\( *([0-9]+) *\) *\+ *vector *\( *([0-9]+) *\)/) - if (m) { - return res.code(200).send(JSON.stringify({ - status: 'success', - data: { - resultType: 'vector', - result: [{ - metric: {}, - value: [Math.floor(Date.now() / 1000), `${parseFloat(m[1]) + parseFloat(m[2])}`] - }] - } - })) - } - /* remove newlines */ - req.query.query = req.query.query.replace(/\n/g, ' ') - /* scan fingerprints */ - /* TODO: handle time tag + direction + limit to govern the query output */ - try { - const response = await instantQueryScan(req.query) - res.code(200) - res.headers({ - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin': CORS - }) - return response - } catch (err) { - asyncLogError(err, req.log) - throw err - } -} - -module.exports = handler diff --git a/lib/handlers/query_range.js b/lib/handlers/query_range.js deleted file mode 100644 index 6939ff87..00000000 --- a/lib/handlers/query_range.js +++ /dev/null @@ -1,54 +0,0 @@ -/* Query Handler */ -/* - For doing queries, accepts the following parameters in the query-string: - query: a logQL query - limit: max number of entries to return - start: the start time for the query, as a nanosecond Unix epoch (nanoseconds since 1970) - end: the end time for the query, as a nanosecond Unix epoch (nanoseconds since 1970) - direction: forward or backward, useful when specifying a limit - regexp: a regex to filter the returned results, will eventually be rolled into the query language -*/ - -const { parseCliQL } = require('../cliql') -const { checkCustomPlugins } = require('./common') -const { asyncLogError, CORS } = require('../../common') -const { scanFingerprints, scanClickhouse } = require('../db/clickhouse') - -async function handler (req, res) { - req.log.debug('GET /loki/api/v1/query_range') - const params = req.query - const resp = { streams: [] } - if (!req.query.query) { - return res.send(resp) - } - /* remove newlines */ - req.query.query = req.query.query.replace(/\n/g, ' ') - if (!req.query.query) { - return res.code(400).send('invalid query') - } - const cliqlParams = parseCliQL(req.query.query) - if (cliqlParams) { - scanClickhouse(cliqlParams, res, params) - return - } - const pluginOut = await checkCustomPlugins(req.query) - if (pluginOut) { - res.header('Content-Type', pluginOut.type) - return res.send(pluginOut.out) - } - req.query.optimizations = true - try { - const response = await scanFingerprints(req.query) - res.code(200) - res.headers({ - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin': CORS - }) - return response - } catch (err) { - asyncLogError(err, req.log) - throw err - } -} - -module.exports = handler diff --git a/lib/handlers/ready.js b/lib/handlers/ready.js deleted file mode 100644 index e9db6d21..00000000 --- a/lib/handlers/ready.js +++ /dev/null @@ -1,17 +0,0 @@ -const { ping, capabilities, ready } = require('../db/clickhouse') -async function handler (req, res) { - try { - if (!ready()) { - return res.send(new Error('qryn not ready')) - } - await ping() - return res.send({ - capabilities: { - LIVE_mode: capabilities.LIVE_mode ? 'longpolling' : 'callback-polling' - } - }) - } catch (e) { - return res.send(new Error('Clickhouse DB not ready')) - } -} -module.exports = handler diff --git a/lib/handlers/series.js b/lib/handlers/series.js deleted file mode 100644 index 8f0bc62f..00000000 --- a/lib/handlers/series.js +++ /dev/null @@ -1,29 +0,0 @@ -const { scanSeries } = require('../db/clickhouse') -const { CORS } = require('../../common') - -// Example Handler -async function handler (req, res) { - const getArray = (val) => { - if (!val) { - return [] - } - return Array.isArray(val) ? val : [val] - } - let match = getArray(req.query.match) - if (!match.length) { - match = getArray(req.query['match[]']) - } - console.log(match) - if (!match.length) { - throw new Error('Match param is required') - } - const response = await scanSeries(match) - res.code(200) - res.headers({ - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin': CORS - }) - return response -} - -module.exports = handler diff --git a/lib/handlers/tags.js b/lib/handlers/tags.js deleted file mode 100644 index 43976fdb..00000000 --- a/lib/handlers/tags.js +++ /dev/null @@ -1,24 +0,0 @@ -/* Tags Label Handler */ -/* - For retrieving the names of the tags tempo can query on. - Responses looks like this: -{ - "tagNames": [ - "instance", - "job", - ... - ] -} -*/ - -const DATABASE = require('../db/clickhouse') -const { labels } = DATABASE.cache - -function handler (req, res) { - req.log.debug('GET /api/search/tags') - const allLabels = labels.get('_LABELS_') - const resp = { tagNames: allLabels } - return res.send(resp) -}; - -module.exports = handler diff --git a/lib/handlers/tags_values.js b/lib/handlers/tags_values.js deleted file mode 100644 index 72a186b1..00000000 --- a/lib/handlers/tags_values.js +++ /dev/null @@ -1,31 +0,0 @@ -/* Tag Value Handler */ -/* - For retrieving the tag values tempo can query on. - Responses looks like this: - { - "tagValues": [ - "default", - "cortex-ops", - ... - ] -} -*/ - -const DATABASE = require('../db/clickhouse') -const { labels } = DATABASE.cache - -function handler (req, res) { - req.log.debug(`GET /api/search/tag/${req.params.name}/values`) - if (req.params.name.includes('.')) { - var tag = req.params.name.split('.').reduce((a, b) => a + b.charAt(0).toUpperCase() + b.slice(1)); - const allValues = labels.get(tag) - const resp = { tagValues: allValues } - return res.send(resp) - } else { - const allValues = labels.get(req.params.name) - const resp = { tagValues: allValues } - return res.send(resp) - } -}; - -module.exports = handler diff --git a/lib/handlers/tail.js b/lib/handlers/tail.js deleted file mode 100644 index 1d06b804..00000000 --- a/lib/handlers/tail.js +++ /dev/null @@ -1,25 +0,0 @@ -const Watcher = require('../db/watcher') -const logger = require('../logger') - -module.exports = function handler (connection, res) { - try { - const w = Watcher.getWatcher(res.query) - const onData = (s) => { - connection.socket.send(s) - } - const onError = err => { - logger.error(err) - connection.socket.send(err) - connection.end() - } - w.on('data', onData) - w.on('error', onError) - connection.socket.on('close', () => { - w.removeListener('data', onData) - w.removeListener('error', onError) - w.destroy() - }) - } catch (err) { - logger.error(err) - } -} diff --git a/lib/handlers/telegraf.js b/lib/handlers/telegraf.js deleted file mode 100644 index a564a05a..00000000 --- a/lib/handlers/telegraf.js +++ /dev/null @@ -1,87 +0,0 @@ -/* Telegraf Handler */ -/* - -[[outputs.http]] - url = "http://qryn:3100/telegraf" - data_format = "json" - method = "POST" - -*/ - -// CANDIDATE TO DELETE DUE TO REPLACE WITH INFLUX_WRITE - -const { asyncLogError, bothType } = require('../../common') -const stringify = require('../utils').stringify -const DATABASE = require('../db/clickhouse') -const { bulk_labels, bulk, labels } = DATABASE.cache -const { fingerPrint } = require('../utils') -const { readonly } = require('../../common') - -function handler (req, res) { - if (!req.body && !req.body.metrics) { - asyncLogError('No Request Body!', req.log) - return - } - if (readonly) { - asyncLogError('Readonly! No push support.', req.log) - return res.send(500) - } - let streams - streams = req.body.metrics - if (!Array.isArray(streams)) streams = [streams] - if (streams) { - req.log.debug({ streams }, 'influx') - streams.forEach(function (stream) { - let JSONLabels - let finger = null - try { - JSONLabels = stream.tags - JSONLabels.metric = stream.name - // Calculate Fingerprint - const strLabels = stringify(JSONLabels) - finger = fingerPrint(strLabels) - req.log.debug({ JSONLabels, finger }, 'LABELS FINGERPRINT') - labels.add(finger.toString(), stream.labels) - // Store Fingerprint - bulk_labels.add(finger.toString(), [ - new Date().toISOString().split('T')[0], - finger, - strLabels, - stream.name || '', - bothType - ]) - for (const key in JSONLabels) { - // req.log.debug({ key, data: JSONLabels[key] }, 'Storing label'); - labels.add('_LABELS_', key) - labels.add(key, JSONLabels[key]) - } - } catch (err) { - asyncLogError(err, req.log) - } - if (stream.fields) { - Object.keys(stream.fields).forEach(function (entry) { - // req.log.debug({ entry, finger }, 'BULK ROW'); - if ( - !entry && - !stream.timestamp && - (!entry.value || !entry.line) - ) { - asyncLogError('no bulkable data', req.log) - return - } - const values = [ - finger, - BigInt(stream.timestamp + '000000000'), - stream.fields[entry] || 0, - stream.fields[entry].toString() || '', - bothType - ] - bulk.add(values) - }) - } - }) - } - return res.send(200) -} - -module.exports = handler diff --git a/lib/handlers/tempo_push.js b/lib/handlers/tempo_push.js deleted file mode 100644 index 091be460..00000000 --- a/lib/handlers/tempo_push.js +++ /dev/null @@ -1,82 +0,0 @@ -/* Zipkin Push Handler - Accepts JSON formatted requests when the header Content-Type: application/json is sent. - Example of the Zipkin span JSON format: - [{ - "id": "1234", - "traceId": "0123456789abcdef", - "timestamp": 1608239395286533, - "duration": 100000, - "name": "span from bash!", - "tags": { - "http.method": "GET", - "http.path": "/api" - }, - "localEndpoint": { - "serviceName": "shell script" - } - }] -*/ - -const { Transform } = require('stream') -const { asyncLogError } = require('../../common') -const { readonly } = require('../../common') -const { pushZipkin } = require('../db/clickhouse') - -function handleOne (req, streams, promises) { - const self = this - streams.on('data', function (stream) { - stream = stream.value - promises.push(pushZipkin([stream])) - }) -} - -async function handler (req, res) { - req.log.debug('POST /tempo/api/push') - if (!req.body) { - asyncLogError('No Request Body!', req.log) - return res.code(500).send() - } - if (readonly) { - asyncLogError('Readonly! No push support.', req.log) - return res.code(500).send() - } - let streams = req.body - if ( - req.headers['content-type'] && - req.headers['content-type'].indexOf('application/x-protobuf') > -1 - ) { - streams = new Transform({ - transform (chunk, encoding, callback) { - callback(chunk) - } - }) - const sendStreams = (async () => { - for (const s of req.body) { - while (!streams.write(s)) { - await new Promise(resolve => streams.once('drain', resolve)) - } - } - })() - handleOne.bind(this)(req, streams) - await sendStreams - req.log.debug({ streams }, 'GOT protoBuf') - } else { - streams = req.body - if (req.body.error) { - throw req.body.error - } - const promises = [] - handleOne.bind(this)(req, streams, promises) - await new Promise((resolve, reject) => { - req.body.once('close', resolve) - req.body.once('end', resolve) - req.body.once('error', reject) - }) - req.log.debug(`waiting for ${promises.length} promises`) - await Promise.all(promises) - } - - return res.code(200).send('OK') -} - -module.exports = handler diff --git a/lib/handlers/tempo_search.js b/lib/handlers/tempo_search.js deleted file mode 100644 index c47a2646..00000000 --- a/lib/handlers/tempo_search.js +++ /dev/null @@ -1,82 +0,0 @@ -/* Qryn Tempo Search Handler */ -/* - Returns JSON formatted results to /api/search API - - { "traces": [{ - "traceID":"AC62F5E32AFE5C28D4F8DCA4C159627E", - "rootServiceName":"dummy-server", - "rootTraceName":"request_response", - "startTimeUnixNano":1661290946168377000, - "durationMs":10 - }] - } - -*/ - -const logfmt = require('logfmt') -const common = require('../../common') -const { asyncLogError, CORS } = require('../../common') -const { scanTempo } = require('../db/clickhouse') -const { search } = require('../../traceql') - -async function handler (req, res) { - req.log.debug('GET /api/search') - if (req.query.q) { - return await searchV2(req, res) - } - const resp = { data: [] } - if (!req.query.tags) { - return res.send(resp) - } - /* transpile trace params to logql selector */ - let tags = logfmt.parse(req.query.tags) - req.query.tags = tags - req.log.debug(tags) - tags = Object.entries(tags).map(e => - `${e[0].replace(/\../, m => `${m}`.toUpperCase().substring(1))}=${JSON.stringify(e[1])}` - ) - req.query.start += '000000000' - req.query.end += '000000000' - req.query.query = `{${tags.join(', ')}}` - if (req.params.traceId) req.query.query += ` |~ "${req.params.traceId}"` - req.query.minDurationNs = req.query.minDuration ? common.durationToNs(req.query.minDuration) : undefined - req.query.maxDurationNs = req.query.maxDuration ? common.durationToNs(req.query.maxDuration) : undefined - - req.log.debug(`Search Tempo ${req.query.query}, ${req.query.start}, ${req.query.end}`) - try { - let resp = await scanTempo( - req.query - ) - resp = [...resp.v2, ...resp.v1] - res.code(200) - res.headers({ - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin': CORS - }) - return { - traces: resp - } - } catch (err) { - asyncLogError(err, req.log) - return res.send(resp) - } -} - -const searchV2 = async (req, res) => { - try { - const query = req.query.q - if (req.query.q === '{}') { - return res.code(200).send(JSON.stringify({ traces: [] })) - } - const limit = req.query.limit || 100 - const start = req.query.start || Math.floor(Date.now() / 1000) - 3600 - const end = req.query.end || Math.floor(Date.now() / 1000) - 3600 - const traces = await search(query, limit, new Date(start * 1000), new Date(end * 1000)) - return res.code(200).send(JSON.stringify({ traces: traces })) - } catch (e) { - req.log.error(e) - return res.code(500).send(e.message) - } -} - -module.exports = handler diff --git a/lib/handlers/tempo_tags.js b/lib/handlers/tempo_tags.js deleted file mode 100644 index fe579eef..00000000 --- a/lib/handlers/tempo_tags.js +++ /dev/null @@ -1,13 +0,0 @@ -const { asyncLogError } = require('../../common') -const { queryTempoTags } = require('../db/clickhouse') -async function handler (req, res) { - try { - const resp = await queryTempoTags() - return res.send(resp.map(e => e.key)) - } catch (e) { - asyncLogError(e, req.log) - res.code(500) - } -} - -module.exports = handler diff --git a/lib/handlers/tempo_traces.js b/lib/handlers/tempo_traces.js deleted file mode 100644 index 3e832495..00000000 --- a/lib/handlers/tempo_traces.js +++ /dev/null @@ -1,243 +0,0 @@ -/* Qryn Tempo Query Handler */ -/* - Returns Protobuf-JSON formatted to /tempo/api/traces API - Protobuf JSON Schema: https://github.com/metrico/qryn/pull/87#issuecomment-1003616559 - API Push Example: https://github.com/metrico/qryn/pull/87#issuecomment-1002683058 - - TODO: - - Refactor code and optimize interfacing with db/clickhouse.js and handler/tempo_push.js - - Optimize for performance and reduce/remove POC debug layers - -*/ - -const protoBuff = require('protobufjs') -const TraceDataType = protoBuff.loadSync(__dirname + '/../opentelemetry/proto/trace/v1/trace.proto') - .lookupType('opentelemetry.proto.trace.v1.TracesData') -const { stringify } = require('logfmt') -const { flatOTLPAttrs, OTLPgetServiceNames } = require('../utils') -const { asyncLogError } = require('../../common') -const { tempoQueryScan } = require('../db/clickhouse') - -function pad (pad, str, padLeft) { - if (typeof str === 'undefined') { - return pad - } - if (padLeft) { - return (pad + str).slice(-pad.length) - } else { - return (str + pad).substring(0, pad.length) - } -} - -function padLeft (size, str) { - return pad((new Array(size)).fill('0').join(''), str, true) -} - -async function handler (req, res) { - req.log.debug('GET /api/traces/:traceId/:json') - const jsonApi = req.params.json || false - const resp = { data: [] } - if (!req.params.traceId) { - return res.send(resp) - } - if (req.params.traceId) { - req.params.traceId = pad('00000000000000000000000000000000', req.params.traceId, true) - } - if (!req.params.traceId.match(/^[0-9a-fA-F]{32}$/) || req.params.traceId?.length !== 32) { - res.code(400) - return res.send(`invalid traceid ${req.params.traceId}`) - } - - /* transpile trace params to logql selector */ - /*if (req.query.tags) { - req.query.query = `{${req.query.tags}}` - if (req.params.traceId) req.query.query += ` |~ "${req.params.traceId}"` - } else if (this.tempo_tagtrace) { - req.query.query = `{traceId="${req.params.traceId}"}` - } else { - req.query.query = `{type="tempo"} |~ "${req.params.traceId}"` - }*/ - - /* TODO: handle time tag + direction + limit to govern the query output */ - try { - const resp = await tempoQueryScan( - req.query, res, req.params.traceId - ) - /* Basic Structure for traces/v1 Protobuf encoder */ - const struct = { resourceSpans: [] } - - /* Reformat data from collected spans (includes overkill steps) */ - resp.v1.forEach(function (span) { - struct.resourceSpans.push(formatSpanV1(span)) - req.log.debug({ span }, 'push span') - }) - const v2SpansByService = resp.v2.reduce((res, span) => { - const _span = formatSpanV2(span, jsonApi) - const svcName = _span.serviceName - res[svcName] = [...(res[svcName] || []), _span] - return res - }, {}) - struct.resourceSpans.push.apply(struct.resourceSpans, - Object.entries(v2SpansByService).map(e => ({ - resource: { - attributes: [ - { - key: 'collector', - value: { - stringValue: 'qryn' - } - }, { key: 'service.name', value: { stringValue: e[0] } }] - }, - instrumentationLibrarySpans: [ - { - instrumentationLibrary: {}, - spans: e[1] - } - ] - }))) - - if (!struct.resourceSpans.length) { - return res.code(404).send('Not found') - } - - if (jsonApi) { - return res.headers({ 'content-type': 'application/json' }).send(struct) - } else { - /* Pack spans into Protobuf response */ - const inside = TraceDataType.fromObject(struct) - const proto = TraceDataType.encode(inside).finish() - res.header('Content-Type', 'application/x-protobuf') - return res.send(proto) - } - } catch (err) { - asyncLogError(err, req.log) - return res.headers({ 'content-type': 'application/json' }).send(resp) - } -} - -function formatSpanV1 (span) { - const attributes = [] - if (span.localEndpoint.serviceName || span.serviceName) { - span.serviceName = span.serviceName || span.localEndpoint.serviceName - attributes.push({ key: 'service.name', value: { stringValue: span.serviceName } }) - } - if (span.traceID) { - const tmp = pad('00000000000000000000000000000000', span.traceID, true) - span.traceId = Buffer.from(tmp, 'hex').toString('base64') - } - if (span.spanID) { - const tmp = pad('0000000000000000', span.spanID, true) - span.spanId = Buffer.from(tmp, 'hex').toString('base64') - } - if (span.parentSpanID) { - const tmp = pad('0000000000000000', span.parentSpanID, true) - span.parentSpanId = Buffer.from(tmp, 'hex').toString('base64') - } - if (span.operationName && !span.name) { - span.name = span.operationName - } - if (span.tags.length > 0) { - span.tags.forEach(function (tag) { - attributes.push({ key: tag.key, value: { stringValue: tag.value || '' } }) - }) - } - /* Form a new span/v1 Protobuf-JSON response object wrapper */ - const protoJSON = { - resource: { - attributes: [ - { - key: 'collector', - value: { - stringValue: 'qryn' - } - }] - }, - instrumentationLibrarySpans: [ - { - instrumentationLibrary: {}, - spans: [span] - } - ] - } - /* Merge Attributes */ - if (attributes.length > 0) protoJSON.resource.attributes = protoJSON.resource.attributes.concat(attributes) - /* Add to Protobuf-JSON struct */ - return protoJSON -} - -function formatSpanV2 (span, json) { - switch (span.payload_type) { - case 1: - return formatZipkinSpanV2(span.payload, json) - case 2: - return formatOTLPSpanV2(span.payload, json) - } -} - -function formatZipkinSpanV2 (span, json) { - const getId = (rawId, size) => json ? rawId : Buffer.from(padLeft(size, rawId), 'hex').toString('base64') - const res = { - traceID: span.traceId, - traceId: span.traceId ? getId(span.traceId, 32) : undefined, - spanID: span.id, - spanId: span.id ? getId(span.id, 16) : undefined, - parentSpanId: span.parentId ? getId(span.parentId, 16) : undefined, - name: span.name || '', - startTimeUnixNano: `${parseInt(span.timestamp)}000`, - endTimeUnixNano: `${parseInt(span.timestamp) + parseInt(span.duration)}000`, - serviceName: '', - attributes: [], - events: (span.annotations || []).map(a => ({ - timeUnixNano: `${parseInt(a.timestamp)}000`, - name: `${a.value || ''}` - })) - } - const attrs = { ...span.tags, name: span.localEndpoint.serviceName } - let serviceName = '' - if (span.localEndpoint?.serviceName) { - attrs['service.name'] = span.localEndpoint.serviceName - serviceName = span.localEndpoint.serviceName - res.serviceName = serviceName - } else if (span.remoteEndpoint?.serviceName) { - attrs['service.name'] = span.remoteEndpoint.serviceName - serviceName = span.remoteEndpoint.serviceName - res.serviceName = serviceName - } - res.attributes = Object.entries(attrs).map(e => ({ key: `${e[0]}`, value: { stringValue: `${e[1]}` } })) - return res -} - -function formatOTLPSpanV2 (span, json) { - const getId = (rawId, size) => rawId - - const flatSpanAttrs = flatOTLPAttrs(span.attributes) - const { local: serviceName, remote: remoteServiceName } = OTLPgetServiceNames(flatSpanAttrs) - - const res = { - traceID: span.traceId, - traceId: span.traceId ? getId(span.traceId, 32) : undefined, - spanID: span.spanId, - spanId: span.spanId ? getId(span.spanId, 16) : undefined, - parentSpanId: span.parentSpanId ? getId(span.parentSpanId, 16) : undefined, - name: span.name || '', - startTimeUnixNano: span.startTimeUnixNano, - endTimeUnixNano: span.endTimeUnixNano, - serviceName: serviceName, - attributes: [ - { key: 'service.name', value: { stringValue: serviceName } }, - remoteServiceName ? { key: 'remoteService.name', value: { stringValue: remoteServiceName } } : null, - ...span.attributes.filter(a => !{ 'service.name': true, 'remoteService.name': true }[a.key]) - ].filter(a => a), - status: span.status || {}, - events: (span.events || []).map(a => ({ - timeUnixNano: a.timeUnixNano, - name: stringify(flatOTLPAttrs([ - ...(a.attributes || []), - { key: 'name', value: { stringValue: a.name } } - ])) - })) - } - return res -} - -module.exports = handler diff --git a/lib/handlers/tempo_v2_tags.js b/lib/handlers/tempo_v2_tags.js deleted file mode 100644 index dafdef03..00000000 --- a/lib/handlers/tempo_v2_tags.js +++ /dev/null @@ -1,13 +0,0 @@ -const { asyncLogError } = require('../../common') -const { queryTempoTags } = require('../db/clickhouse') -async function handler (req, res) { - try { - const resp = await queryTempoTags() - return res.send({ scopes: [{ name: 'span', tags: resp.map(e => `${e.key}`) }] }) - } catch (e) { - asyncLogError(e, req.log) - res.code(500) - } -} - -module.exports = handler diff --git a/lib/handlers/tempo_v2_values.js b/lib/handlers/tempo_v2_values.js deleted file mode 100644 index 7df6cf82..00000000 --- a/lib/handlers/tempo_v2_values.js +++ /dev/null @@ -1,33 +0,0 @@ -/* Tag Value Handler V2 */ -/* - For retrieving the tag values tempo can query on. - Responses looks like this: -{ - "tagValues": [ - { - "type": "string", - "value": "a" - }, - .... - ] -} -*/ -const { asyncLogError } = require('../../common') -const { queryTempoValues } = require('../db/clickhouse') - -async function handler (req, res) { - req.log.debug(`GET /api/v2/search/tag/${req.params.name}/values`) - if (!req.params.name) { - return res.send({ tagValues: [] }) - } - try { - req.params.name = req.params.name.replace(/^resource\.|^span\./, '') - const vals = (await queryTempoValues(req.params.name)).map(e => e.val) - return res.send({ tagValues: vals.map(v => ({ type: 'string', value: v })) }) - } catch (e) { - asyncLogError(e, req.log) - res.code(500) - } -}; - -module.exports = handler diff --git a/lib/handlers/tempo_values.js b/lib/handlers/tempo_values.js deleted file mode 100644 index e84eff0d..00000000 --- a/lib/handlers/tempo_values.js +++ /dev/null @@ -1,30 +0,0 @@ -/* Tag Value Handler */ -/* - For retrieving the tag values tempo can query on. - Responses looks like this: - { - "tagValues": [ - "default", - "cortex-ops", - ... - ] -} -*/ -const { asyncLogError } = require('../../common') -const { queryTempoValues } = require('../db/clickhouse') - -async function handler (req, res) { - req.log.debug(`GET /api/search/tag/${req.params.name}/values`) - if (!req.params.name) { - return res.send({ tagValues: [] }) - } - try { - const vals = (await queryTempoValues(req.params.name)).map(e => e.val) - return res.send({ tagValues: vals }) - } catch (e) { - asyncLogError(e, req.log) - res.code(500) - } -}; - -module.exports = handler diff --git a/lib/influx/Readme.md b/lib/influx/Readme.md deleted file mode 100644 index a10bb486..00000000 --- a/lib/influx/Readme.md +++ /dev/null @@ -1,32 +0,0 @@ -# Influx parser powered by TinyGo / WASM - -## API - -`async init()` - initialize WASM - -`parse('<INFLUX LINE REQUEST>')` - parse influx request to JSON - -- output: `[{timestamp: "<timestmap in ns>", measurement: "<measurement>", tags:{tag1: "val1"}, fields:{f1: "v1"}]` - -NOTE: Currently supports only `ns` precision!!! - -## Example - -```javascript -const parser = require('./index'); -(async() => { - await parser.init(); - console.log(parser.parse(`m1,t1=v1,t2=v2 message="message with spaces -and linebreaks" 1675254420130588000`)); -})(); -``` - -## Build - -### Prerequisites -- golang 1.19 -- tiny-go v0.26.0 - -### Build cmd - -`tinygo build -o wasm.wasm -target wasm .` diff --git a/lib/influx/go.mod b/lib/influx/go.mod deleted file mode 100644 index 09afb1c0..00000000 --- a/lib/influx/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module influx - -go 1.19 - -require github.com/influxdata/telegraf v1.23.3 diff --git a/lib/influx/go.sum b/lib/influx/go.sum deleted file mode 100644 index 04b30534..00000000 --- a/lib/influx/go.sum +++ /dev/null @@ -1,35 +0,0 @@ -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= -github.com/Microsoft/go-winio v0.4.17 h1:iT12IBVClFevaf8PuVyi3UmZOVh4OqnaLxDTW2O6j3w= -github.com/Microsoft/hcsshim v0.8.24 h1:jP+GMeRXIR1sH1kG4lJr9ShmSjVrua5jmFZDtfYGkn4= -github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= -github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= -github.com/containerd/containerd v1.5.13 h1:XqvKw9i4P7/mFrC3TSM7yV5cwFZ9avXe6M3YANKnzEE= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= -github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/influxdata/telegraf v1.23.3 h1:obFbkBCAWKQbJGcYqFITHtmxxQ8/nE9pqvZQ3Uhsnnw= -github.com/influxdata/telegraf v1.23.3/go.mod h1:DA7kymhpNGAHGFW8sTW5Lg0jPQ4ff6XOVwiQrjr4ipg= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= -github.com/moby/sys/mount v0.2.0 h1:WhCW5B355jtxndN5ovugJlMFJawbUODuW8fSnEH6SSM= -github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= -github.com/opencontainers/runc v1.1.2 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM= -github.com/testcontainers/testcontainers-go v0.12.0 h1:SK0NryGHIx7aifF6YqReORL18aGAA4bsDPtikDVCEyg= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d h1:Zu/JngovGLVi6t2J3nmAf3AoTDwuzw85YZ3b9o4yU7s= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/lib/influx/index.js b/lib/influx/index.js deleted file mode 100644 index aa4c3e13..00000000 --- a/lib/influx/index.js +++ /dev/null @@ -1,37 +0,0 @@ -require('./wasm_exec') -const fs = require('fs') -let wasm - - -const init = async () => { - if (wasm) { - return - } - const go = new Go(); - const obj = await WebAssembly.instantiate(fs.readFileSync(__dirname + '/wasm.wasm'), go.importObject) - wasm = obj.instance -} - -const parse = (str) => { - const buff = (new TextEncoder()).encode(str) - const inAddr = wasm.exports.CreateBuff(buff.length) - let mem = new Uint8Array(wasm.exports.memory.buffer) - let inArr = mem.subarray(inAddr, inAddr + buff.length) - inArr.set(buff, 0) - wasm.exports.ParseBytes() - const outAddr = wasm.exports.GetResp() - mem = new Uint8Array(wasm.exports.memory.buffer) - let outArr = mem.subarray(outAddr, outAddr + wasm.exports.GetLen()) - let resp = (new TextDecoder()).decode(outArr) - wasm.exports.Free() - resp = JSON.parse(resp) - if (resp.error) { - throw new Error(resp.error) - } - return resp -} - -module.exports = { - parse, - init -} diff --git a/lib/influx/main.go b/lib/influx/main.go deleted file mode 100644 index db7d5ee3..00000000 --- a/lib/influx/main.go +++ /dev/null @@ -1,86 +0,0 @@ -package main - -import ( - "fmt" - telegraf "github.com/influxdata/telegraf" - "github.com/influxdata/telegraf/plugins/parsers/influx" - "strconv" - "strings" - "time" -) - -func main() {} - -var buff []byte -var err error -var metrics []telegraf.Metric -var resp []byte - -//export CreateBuff -func CreateBuff(len int32) *byte { - buff = make([]byte, len) - return &buff[0] -} - -//export ParseBytes -func ParseBytes() { - hndl := influx.NewMetricHandler() - hndl.SetTimePrecision(time.Nanosecond) - parser := influx.NewParser(hndl) - metrics, err = parser.Parse(buff) - if err != nil { - resp = []byte(fmt.Sprintf(`{"error": %s}`, strconv.Quote(err.Error()))) - return - } - var mapRes []string - for _, m := range metrics { - var tags []string - for _, t := range m.TagList() { - tags = append(tags, fmt.Sprintf("%s:%s", - strconv.Quote(t.Key), - strconv.Quote(t.Value))) - } - var fields []string - for k, f := range m.Fields() { - var strField string - switch f.(type) { - case int64: - strField = strconv.FormatInt(f.(int64), 10) - case float64: - strField = strconv.FormatFloat(f.(float64), 'f', 10, 64) - case string: - strField = strconv.Quote(f.(string)) - case bool: - strField = strconv.FormatBool(f.(bool)) - } - if strField != "" { - fields = append(fields, fmt.Sprintf("%s:%s", strconv.Quote(k), strField)) - } - } - ent := fmt.Sprintf(`{"timestamp":"%d","measurement": %s, "tags": {%s}, "fields": {%s}}`, - m.Time().UnixNano(), - strconv.Quote(m.Name()), - strings.Join(tags, ","), - strings.Join(fields, ",")) - mapRes = append(mapRes, ent) - } - resp = []byte(fmt.Sprintf("[%s]", strings.Join(mapRes, ","))) -} - -//export GetLen -func GetLen() int32 { - return int32(len(resp)) -} - -//export GetResp -func GetResp() *byte { - return &resp[0] -} - -//export Free -func Free() { - buff = nil - err = nil - metrics = nil - resp = nil -} diff --git a/lib/influx/wasm.wasm b/lib/influx/wasm.wasm deleted file mode 100755 index ce6e1d26..00000000 Binary files a/lib/influx/wasm.wasm and /dev/null differ diff --git a/lib/influx/wasm_exec.js b/lib/influx/wasm_exec.js deleted file mode 100644 index 8021b44e..00000000 --- a/lib/influx/wasm_exec.js +++ /dev/null @@ -1,535 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// -// This file has been modified for use by the TinyGo compiler. - -(() => { - // Map multiple JavaScript environments to a single common API, - // preferring web standards over Node.js API. - // - // Environments considered: - // - Browsers - // - Node.js - // - Electron - // - Parcel - - if (typeof global !== "undefined") { - // global already exists - } else if (typeof window !== "undefined") { - window.global = window; - } else if (typeof self !== "undefined") { - self.global = self; - } else { - throw new Error("cannot export Go (neither global, window nor self is defined)"); - } - - if (!global.require && typeof require !== "undefined") { - global.require = require; - } - - if (!global.fs && global.require) { - global.fs = require("fs"); - } - - const enosys = () => { - const err = new Error("not implemented"); - err.code = "ENOSYS"; - return err; - }; - - if (!global.fs) { - let outputBuf = ""; - global.fs = { - constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused - writeSync(fd, buf) { - outputBuf += decoder.decode(buf); - const nl = outputBuf.lastIndexOf("\n"); - if (nl != -1) { - console.log(outputBuf.substr(0, nl)); - outputBuf = outputBuf.substr(nl + 1); - } - return buf.length; - }, - write(fd, buf, offset, length, position, callback) { - if (offset !== 0 || length !== buf.length || position !== null) { - callback(enosys()); - return; - } - const n = this.writeSync(fd, buf); - callback(null, n); - }, - chmod(path, mode, callback) { callback(enosys()); }, - chown(path, uid, gid, callback) { callback(enosys()); }, - close(fd, callback) { callback(enosys()); }, - fchmod(fd, mode, callback) { callback(enosys()); }, - fchown(fd, uid, gid, callback) { callback(enosys()); }, - fstat(fd, callback) { callback(enosys()); }, - fsync(fd, callback) { callback(null); }, - ftruncate(fd, length, callback) { callback(enosys()); }, - lchown(path, uid, gid, callback) { callback(enosys()); }, - link(path, link, callback) { callback(enosys()); }, - lstat(path, callback) { callback(enosys()); }, - mkdir(path, perm, callback) { callback(enosys()); }, - open(path, flags, mode, callback) { callback(enosys()); }, - read(fd, buffer, offset, length, position, callback) { callback(enosys()); }, - readdir(path, callback) { callback(enosys()); }, - readlink(path, callback) { callback(enosys()); }, - rename(from, to, callback) { callback(enosys()); }, - rmdir(path, callback) { callback(enosys()); }, - stat(path, callback) { callback(enosys()); }, - symlink(path, link, callback) { callback(enosys()); }, - truncate(path, length, callback) { callback(enosys()); }, - unlink(path, callback) { callback(enosys()); }, - utimes(path, atime, mtime, callback) { callback(enosys()); }, - }; - } - - if (!global.process) { - global.process = { - getuid() { return -1; }, - getgid() { return -1; }, - geteuid() { return -1; }, - getegid() { return -1; }, - getgroups() { throw enosys(); }, - pid: -1, - ppid: -1, - umask() { throw enosys(); }, - cwd() { throw enosys(); }, - chdir() { throw enosys(); }, - } - } - - if (!global.crypto) { - const nodeCrypto = require("crypto"); - global.crypto = { - getRandomValues(b) { - nodeCrypto.randomFillSync(b); - }, - }; - } - - if (!global.performance) { - global.performance = { - now() { - const [sec, nsec] = process.hrtime(); - return sec * 1000 + nsec / 1000000; - }, - }; - } - - if (!global.TextEncoder) { - global.TextEncoder = require("util").TextEncoder; - } - - if (!global.TextDecoder) { - global.TextDecoder = require("util").TextDecoder; - } - - // End of polyfills for common API. - - const encoder = new TextEncoder("utf-8"); - const decoder = new TextDecoder("utf-8"); - var logLine = []; - - global.Go = class { - constructor() { - this._callbackTimeouts = new Map(); - this._nextCallbackTimeoutID = 1; - - const mem = () => { - // The buffer may change when requesting more memory. - return new DataView(this._inst.exports.memory.buffer); - } - - const setInt64 = (addr, v) => { - mem().setUint32(addr + 0, v, true); - mem().setUint32(addr + 4, Math.floor(v / 4294967296), true); - } - - const getInt64 = (addr) => { - const low = mem().getUint32(addr + 0, true); - const high = mem().getInt32(addr + 4, true); - return low + high * 4294967296; - } - - const loadValue = (addr) => { - const f = mem().getFloat64(addr, true); - if (f === 0) { - return undefined; - } - if (!isNaN(f)) { - return f; - } - - const id = mem().getUint32(addr, true); - return this._values[id]; - } - - const storeValue = (addr, v) => { - const nanHead = 0x7FF80000; - - if (typeof v === "number") { - if (isNaN(v)) { - mem().setUint32(addr + 4, nanHead, true); - mem().setUint32(addr, 0, true); - return; - } - if (v === 0) { - mem().setUint32(addr + 4, nanHead, true); - mem().setUint32(addr, 1, true); - return; - } - mem().setFloat64(addr, v, true); - return; - } - - switch (v) { - case undefined: - mem().setFloat64(addr, 0, true); - return; - case null: - mem().setUint32(addr + 4, nanHead, true); - mem().setUint32(addr, 2, true); - return; - case true: - mem().setUint32(addr + 4, nanHead, true); - mem().setUint32(addr, 3, true); - return; - case false: - mem().setUint32(addr + 4, nanHead, true); - mem().setUint32(addr, 4, true); - return; - } - - let id = this._ids.get(v); - if (id === undefined) { - id = this._idPool.pop(); - if (id === undefined) { - id = this._values.length; - } - this._values[id] = v; - this._goRefCounts[id] = 0; - this._ids.set(v, id); - } - this._goRefCounts[id]++; - let typeFlag = 1; - switch (typeof v) { - case "string": - typeFlag = 2; - break; - case "symbol": - typeFlag = 3; - break; - case "function": - typeFlag = 4; - break; - } - mem().setUint32(addr + 4, nanHead | typeFlag, true); - mem().setUint32(addr, id, true); - } - - const loadSlice = (array, len, cap) => { - return new Uint8Array(this._inst.exports.memory.buffer, array, len); - } - - const loadSliceOfValues = (array, len, cap) => { - const a = new Array(len); - for (let i = 0; i < len; i++) { - a[i] = loadValue(array + i * 8); - } - return a; - } - - const loadString = (ptr, len) => { - return decoder.decode(new DataView(this._inst.exports.memory.buffer, ptr, len)); - } - - const timeOrigin = Date.now() - performance.now(); - this.importObject = { - wasi_snapshot_preview1: { - // https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#fd_write - fd_write: function(fd, iovs_ptr, iovs_len, nwritten_ptr) { - let nwritten = 0; - if (fd == 1) { - for (let iovs_i=0; iovs_i<iovs_len;iovs_i++) { - let iov_ptr = iovs_ptr+iovs_i*8; // assuming wasm32 - let ptr = mem().getUint32(iov_ptr + 0, true); - let len = mem().getUint32(iov_ptr + 4, true); - nwritten += len; - for (let i=0; i<len; i++) { - let c = mem().getUint8(ptr+i); - if (c == 13) { // CR - // ignore - } else if (c == 10) { // LF - // write line - let line = decoder.decode(new Uint8Array(logLine)); - logLine = []; - console.log(line); - } else { - logLine.push(c); - } - } - } - } else { - console.error('invalid file descriptor:', fd); - } - mem().setUint32(nwritten_ptr, nwritten, true); - return 0; - }, - fd_close: () => 0, // dummy - fd_fdstat_get: () => 0, // dummy - fd_seek: () => 0, // dummy - "proc_exit": (code) => { - if (global.process) { - // Node.js - process.exit(code); - } else { - // Can't exit in a browser. - throw 'trying to exit with code ' + code; - } - }, - random_get: (bufPtr, bufLen) => { - crypto.getRandomValues(loadSlice(bufPtr, bufLen)); - return 0; - }, - }, - env: { - // func ticks() float64 - "runtime.ticks": () => { - return timeOrigin + performance.now(); - }, - - // func sleepTicks(timeout float64) - "runtime.sleepTicks": (timeout) => { - // Do not sleep, only reactivate scheduler after the given timeout. - setTimeout(this._inst.exports.go_scheduler, timeout); - }, - - // func finalizeRef(v ref) - "syscall/js.finalizeRef": (sp) => { - // Note: TinyGo does not support finalizers so this should never be - // called. - console.error('syscall/js.finalizeRef not implemented'); - }, - - // func stringVal(value string) ref - "syscall/js.stringVal": (ret_ptr, value_ptr, value_len) => { - const s = loadString(value_ptr, value_len); - storeValue(ret_ptr, s); - }, - - // func valueGet(v ref, p string) ref - "syscall/js.valueGet": (retval, v_addr, p_ptr, p_len) => { - let prop = loadString(p_ptr, p_len); - let value = loadValue(v_addr); - let result = Reflect.get(value, prop); - storeValue(retval, result); - }, - - // func valueSet(v ref, p string, x ref) - "syscall/js.valueSet": (v_addr, p_ptr, p_len, x_addr) => { - const v = loadValue(v_addr); - const p = loadString(p_ptr, p_len); - const x = loadValue(x_addr); - Reflect.set(v, p, x); - }, - - // func valueDelete(v ref, p string) - "syscall/js.valueDelete": (v_addr, p_ptr, p_len) => { - const v = loadValue(v_addr); - const p = loadString(p_ptr, p_len); - Reflect.deleteProperty(v, p); - }, - - // func valueIndex(v ref, i int) ref - "syscall/js.valueIndex": (ret_addr, v_addr, i) => { - storeValue(ret_addr, Reflect.get(loadValue(v_addr), i)); - }, - - // valueSetIndex(v ref, i int, x ref) - "syscall/js.valueSetIndex": (v_addr, i, x_addr) => { - Reflect.set(loadValue(v_addr), i, loadValue(x_addr)); - }, - - // func valueCall(v ref, m string, args []ref) (ref, bool) - "syscall/js.valueCall": (ret_addr, v_addr, m_ptr, m_len, args_ptr, args_len, args_cap) => { - const v = loadValue(v_addr); - const name = loadString(m_ptr, m_len); - const args = loadSliceOfValues(args_ptr, args_len, args_cap); - try { - const m = Reflect.get(v, name); - storeValue(ret_addr, Reflect.apply(m, v, args)); - mem().setUint8(ret_addr + 8, 1); - } catch (err) { - storeValue(ret_addr, err); - mem().setUint8(ret_addr + 8, 0); - } - }, - - // func valueInvoke(v ref, args []ref) (ref, bool) - "syscall/js.valueInvoke": (ret_addr, v_addr, args_ptr, args_len, args_cap) => { - try { - const v = loadValue(v_addr); - const args = loadSliceOfValues(args_ptr, args_len, args_cap); - storeValue(ret_addr, Reflect.apply(v, undefined, args)); - mem().setUint8(ret_addr + 8, 1); - } catch (err) { - storeValue(ret_addr, err); - mem().setUint8(ret_addr + 8, 0); - } - }, - - // func valueNew(v ref, args []ref) (ref, bool) - "syscall/js.valueNew": (ret_addr, v_addr, args_ptr, args_len, args_cap) => { - const v = loadValue(v_addr); - const args = loadSliceOfValues(args_ptr, args_len, args_cap); - try { - storeValue(ret_addr, Reflect.construct(v, args)); - mem().setUint8(ret_addr + 8, 1); - } catch (err) { - storeValue(ret_addr, err); - mem().setUint8(ret_addr+ 8, 0); - } - }, - - // func valueLength(v ref) int - "syscall/js.valueLength": (v_addr) => { - return loadValue(v_addr).length; - }, - - // valuePrepareString(v ref) (ref, int) - "syscall/js.valuePrepareString": (ret_addr, v_addr) => { - const s = String(loadValue(v_addr)); - const str = encoder.encode(s); - storeValue(ret_addr, str); - setInt64(ret_addr + 8, str.length); - }, - - // valueLoadString(v ref, b []byte) - "syscall/js.valueLoadString": (v_addr, slice_ptr, slice_len, slice_cap) => { - const str = loadValue(v_addr); - loadSlice(slice_ptr, slice_len, slice_cap).set(str); - }, - - // func valueInstanceOf(v ref, t ref) bool - "syscall/js.valueInstanceOf": (v_addr, t_addr) => { - return loadValue(v_addr) instanceof loadValue(t_addr); - }, - - // func copyBytesToGo(dst []byte, src ref) (int, bool) - "syscall/js.copyBytesToGo": (ret_addr, dest_addr, dest_len, dest_cap, source_addr) => { - let num_bytes_copied_addr = ret_addr; - let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable - - const dst = loadSlice(dest_addr, dest_len); - const src = loadValue(source_addr); - if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) { - mem().setUint8(returned_status_addr, 0); // Return "not ok" status - return; - } - const toCopy = src.subarray(0, dst.length); - dst.set(toCopy); - setInt64(num_bytes_copied_addr, toCopy.length); - mem().setUint8(returned_status_addr, 1); // Return "ok" status - }, - - // copyBytesToJS(dst ref, src []byte) (int, bool) - // Originally copied from upstream Go project, then modified: - // https://github.com/golang/go/blob/3f995c3f3b43033013013e6c7ccc93a9b1411ca9/misc/wasm/wasm_exec.js#L404-L416 - "syscall/js.copyBytesToJS": (ret_addr, dest_addr, source_addr, source_len, source_cap) => { - let num_bytes_copied_addr = ret_addr; - let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable - - const dst = loadValue(dest_addr); - const src = loadSlice(source_addr, source_len); - if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) { - mem().setUint8(returned_status_addr, 0); // Return "not ok" status - return; - } - const toCopy = src.subarray(0, dst.length); - dst.set(toCopy); - setInt64(num_bytes_copied_addr, toCopy.length); - mem().setUint8(returned_status_addr, 1); // Return "ok" status - }, - } - }; - } - - async run(instance) { - this._inst = instance; - this._values = [ // JS values that Go currently has references to, indexed by reference id - NaN, - 0, - null, - true, - false, - global, - this, - ]; - this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id - this._ids = new Map(); // mapping from JS values to reference ids - this._idPool = []; // unused ids that have been garbage collected - this.exited = false; // whether the Go program has exited - - const mem = new DataView(this._inst.exports.memory.buffer) - - while (true) { - const callbackPromise = new Promise((resolve) => { - this._resolveCallbackPromise = () => { - if (this.exited) { - throw new Error("bad callback: Go program has already exited"); - } - setTimeout(resolve, 0); // make sure it is asynchronous - }; - }); - this._inst.exports._start(); - if (this.exited) { - break; - } - await callbackPromise; - } - } - - _resume() { - if (this.exited) { - throw new Error("Go program has already exited"); - } - this._inst.exports.resume(); - if (this.exited) { - this._resolveExitPromise(); - } - } - - _makeFuncWrapper(id) { - const go = this; - return function () { - const event = { id: id, this: this, args: arguments }; - go._pendingEvent = event; - go._resume(); - return event.result; - }; - } - } - - if ( - global.require && - global.require.main === module && - global.process && - global.process.versions && - !global.process.versions.electron - ) { - if (process.argv.length != 3) { - console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); - process.exit(1); - } - - const go = new Go(); - WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { - return go.run(result.instance); - }).catch((err) => { - console.error(err); - process.exit(1); - }); - } -})(); diff --git a/lib/logger.js b/lib/logger.js deleted file mode 100644 index 8cf4b5b2..00000000 --- a/lib/logger.js +++ /dev/null @@ -1,26 +0,0 @@ -/* Logging */ -const pino = require('pino') - -let level = process.env.LOG_LEVEL || 'info' - -if (process.env.DEBUG && !process.env.LOG_LEVEL) { - level = 'debug' -} - -const logger = pino({ - name: 'qryn', - level, - serializers: { - err: pino.stdSerializers.wrapErrorSerializer((err) => { - if (err.response) { - err.responseStatus = err.response.status - const res = new Error(`${err.message}\nResponse: [${err.response.status}] ${err.responseData}`) - res.stack = err.stack - return res.toString() + '\n' + res.stack.toString() - } - return err.message.toString() + (err.stack ? '\n' + err.stack.toString() : '') - }) - } -}) - -module.exports = logger diff --git a/lib/logproto.proto b/lib/logproto.proto deleted file mode 100644 index a5162b6e..00000000 --- a/lib/logproto.proto +++ /dev/null @@ -1,59 +0,0 @@ -syntax = "proto3"; - -package logproto; - -import "google/protobuf/timestamp.proto"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -service Pusher { - rpc Push(PushRequest) returns (PushResponse) {}; -} - -service Querier { - rpc Query(QueryRequest) returns (stream QueryResponse) {}; - rpc Label(LabelRequest) returns (LabelResponse) {}; -} - -message PushRequest { - repeated Stream streams = 1 [(gogoproto.jsontag) = "streams"]; -} - -message PushResponse { -} - -message QueryRequest { - string query = 1; - uint32 limit = 2; - google.protobuf.Timestamp start = 3 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - google.protobuf.Timestamp end = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - Direction direction = 5; - string regex = 6; -} - -enum Direction { - FORWARD = 0; - BACKWARD = 1; -} - -message QueryResponse { - repeated Stream streams = 1; -} - -message LabelRequest { - string name = 1; - bool values = 2; // True to fetch label values, false for fetch labels names. -} - -message LabelResponse { - repeated string values = 1; -} - -message Stream { - string labels = 1 [(gogoproto.jsontag) = "labels"]; - repeated Entry entries = 2 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "entries"]; -} - -message Entry { - google.protobuf.Timestamp timestamp = 1 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (gogoproto.jsontag) = "ts"]; - string line = 2 [(gogoproto.jsontag) = "line"]; -} diff --git a/lib/opentelemetry/proto/trace/v1/trace.proto b/lib/opentelemetry/proto/trace/v1/trace.proto deleted file mode 100644 index 00d6b103..00000000 --- a/lib/opentelemetry/proto/trace/v1/trace.proto +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright 2019, OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package opentelemetry.proto.trace.v1; - -option java_multiple_files = true; -option java_package = "io.opentelemetry.proto.trace.v1"; -option java_outer_classname = "TraceProto"; -option go_package = "github.com/open-telemetry/opentelemetry-proto/gen/go/trace/v1"; - -// TracesData represents the traces data that can be stored in a persistent storage, -// OR can be embedded by other protocols that transfer OTLP traces data but do -// not implement the OTLP protocol. -// -// The main difference between this message and collector protocol is that -// in this message there will not be any "control" or "metadata" specific to -// OTLP protocol. -// -// When new fields are added into this message, the OTLP request MUST be updated -// as well. - - -message TracesData { - // An array of ResourceSpans. - // For data coming from a single resource this array will typically contain - // one element. Intermediary nodes that receive data from multiple origins - // typically batch the data before forwarding further and in that case this - // array will contain multiple elements. - repeated ResourceSpans resource_spans = 1; -} - -// A collection of InstrumentationLibrarySpans from a Resource. -message ResourceSpans { - // The resource for the spans in this message. - // If this field is not set then no resource info is known. - opentelemetry_proto_resource_v1_Resource resource = 1; - - // A list of InstrumentationLibrarySpans that originate from a resource. - repeated InstrumentationLibrarySpans instrumentation_library_spans = 2; - - // This schema_url applies to the data in the "resource" field. It does not apply - // to the data in the "instrumentation_library_spans" field which have their own - // schema_url field. - string schema_url = 3; -} - -// A collection of Spans produced by an InstrumentationLibrary. -message InstrumentationLibrarySpans { - // The instrumentation library information for the spans in this message. - // Semantically when InstrumentationLibrary isn't set, it is equivalent with - // an empty instrumentation library name (unknown). - InstrumentationLibrary instrumentation_library = 1; - - // A list of Spans that originate from an instrumentation library. - repeated Span spans = 2; - - // This schema_url applies to all spans and span events in the "spans" field. - string schema_url = 3; -} - -// Span represents a single operation within a trace. Spans can be -// nested to form a trace tree. Spans may also be linked to other spans -// from the same or different trace and form graphs. Often, a trace -// contains a root span that describes the end-to-end latency, and one -// or more subspans for its sub-operations. A trace can also contain -// multiple root spans, or none at all. Spans do not need to be -// contiguous - there may be gaps or overlaps between spans in a trace. -// -// The next available field id is 17. -message Span { - // A unique identifier for a trace. All spans from the same trace share - // the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes - // is considered invalid. - // - // This field is semantically required. Receiver should generate new - // random trace_id if empty or invalid trace_id was received. - // - // This field is required. - bytes trace_id = 1; - - // A unique identifier for a span within a trace, assigned when the span - // is created. The ID is an 8-byte array. An ID with all zeroes is considered - // invalid. - // - // This field is semantically required. Receiver should generate new - // random span_id if empty or invalid span_id was received. - // - // This field is required. - bytes span_id = 2; - - // trace_state conveys information about request position in multiple distributed tracing graphs. - // It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header - // See also https://github.com/w3c/distributed-tracing for more details about this field. - string trace_state = 3; - - // The `span_id` of this span's parent span. If this is a root span, then this - // field must be empty. The ID is an 8-byte array. - bytes parent_span_id = 4; - - // A description of the span's operation. - // - // For example, the name can be a qualified method name or a file name - // and a line number where the operation is called. A best practice is to use - // the same display name at the same call point in an application. - // This makes it easier to correlate spans in different traces. - // - // This field is semantically required to be set to non-empty string. - // Empty value is equivalent to an unknown span name. - // - // This field is required. - string name = 5; - - // SpanKind is the type of span. Can be used to specify additional relationships between spans - // in addition to a parent/child relationship. - enum SpanKind { - // Unspecified. Do NOT use as default. - // Implementations MAY assume SpanKind to be INTERNAL when receiving UNSPECIFIED. - SPAN_KIND_UNSPECIFIED = 0; - - // Indicates that the span represents an internal operation within an application, - // as opposed to an operation happening at the boundaries. Default value. - SPAN_KIND_INTERNAL = 1; - - // Indicates that the span covers server-side handling of an RPC or other - // remote network request. - SPAN_KIND_SERVER = 2; - - // Indicates that the span describes a request to some remote service. - SPAN_KIND_CLIENT = 3; - - // Indicates that the span describes a producer sending a message to a broker. - // Unlike CLIENT and SERVER, there is often no direct critical path latency relationship - // between producer and consumer spans. A PRODUCER span ends when the message was accepted - // by the broker while the logical processing of the message might span a much longer time. - SPAN_KIND_PRODUCER = 4; - - // Indicates that the span describes consumer receiving a message from a broker. - // Like the PRODUCER kind, there is often no direct critical path latency relationship - // between producer and consumer spans. - SPAN_KIND_CONSUMER = 5; - } - - // Distinguishes between spans generated in a particular context. For example, - // two spans with the same name may be distinguished using `CLIENT` (caller) - // and `SERVER` (callee) to identify queueing latency associated with the span. - SpanKind kind = 6; - - // start_time_unix_nano is the start time of the span. On the client side, this is the time - // kept by the local machine where the span execution starts. On the server side, this - // is the time when the server's application handler starts running. - // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. - // - // This field is semantically required and it is expected that end_time >= start_time. - fixed64 start_time_unix_nano = 7; - - // end_time_unix_nano is the end time of the span. On the client side, this is the time - // kept by the local machine where the span execution ends. On the server side, this - // is the time when the server application handler stops running. - // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. - // - // This field is semantically required and it is expected that end_time >= start_time. - fixed64 end_time_unix_nano = 8; - - // attributes is a collection of key/value pairs. Note, global attributes - // like server name can be set using the resource API. Examples of attributes: - // - // "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" - // "/http/server_latency": 300 - // "abc.com/myattribute": true - // "abc.com/score": 10.239 - // - // The OpenTelemetry API specification further restricts the allowed value types: - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/common.md#attributes - repeated KeyValue attributes = 9; - - // dropped_attributes_count is the number of attributes that were discarded. Attributes - // can be discarded because their keys are too long or because there are too many - // attributes. If this value is 0, then no attributes were dropped. - uint32 dropped_attributes_count = 10; - - // Event is a time-stamped annotation of the span, consisting of user-supplied - // text description and key-value pairs. - message Event { - // time_unix_nano is the time the event occurred. - fixed64 time_unix_nano = 1; - - // name of the event. - // This field is semantically required to be set to non-empty string. - string name = 2; - - // attributes is a collection of attribute key/value pairs on the event. - repeated KeyValue attributes = 3; - - // dropped_attributes_count is the number of dropped attributes. If the value is 0, - // then no attributes were dropped. - uint32 dropped_attributes_count = 4; - } - - // events is a collection of Event items. - repeated Event events = 11; - - // dropped_events_count is the number of dropped events. If the value is 0, then no - // events were dropped. - uint32 dropped_events_count = 12; - - // A pointer from the current span to another span in the same trace or in a - // different trace. For example, this can be used in batching operations, - // where a single batch handler processes multiple requests from different - // traces or when the handler receives a request from a different project. - message Link { - // A unique identifier of a trace that this linked span is part of. The ID is a - // 16-byte array. - bytes trace_id = 1; - - // A unique identifier for the linked span. The ID is an 8-byte array. - bytes span_id = 2; - - // The trace_state associated with the link. - string trace_state = 3; - - // attributes is a collection of attribute key/value pairs on the link. - repeated KeyValue attributes = 4; - - // dropped_attributes_count is the number of dropped attributes. If the value is 0, - // then no attributes were dropped. - uint32 dropped_attributes_count = 5; - } - - // links is a collection of Links, which are references from this span to a span - // in the same or different trace. - repeated Link links = 13; - - // dropped_links_count is the number of dropped links after the maximum size was - // enforced. If this value is 0, then no links were dropped. - uint32 dropped_links_count = 14; - - // An optional final status for this span. Semantically when Status isn't set, it means - // span's status code is unset, i.e. assume STATUS_CODE_UNSET (code = 0). - Status status = 15; -} - -// The Status type defines a logical error model that is suitable for different -// programming environments, including REST APIs and RPC APIs. -message Status { - reserved 1; - - // A developer-facing human readable error message. - string message = 2; - - // For the semantics of status codes see - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#set-status - enum StatusCode { - // The default status. - STATUS_CODE_UNSET = 0; - // The Span has been validated by an Application developers or Operator to have - // completed successfully. - STATUS_CODE_OK = 1; - // The Span contains an error. - STATUS_CODE_ERROR = 2; - }; - - // The status code. - StatusCode code = 3; -} - -// EXTERNAL INCLUDES -// Resource information. -message opentelemetry_proto_resource_v1_Resource { - // Set of labels that describe the resource. - repeated KeyValue attributes = 1; - - // dropped_attributes_count is the number of dropped attributes. If the value is 0, then - // no attributes were dropped. - uint32 dropped_attributes_count = 2; -} - -message AnyValue { - // The value is one of the listed fields. It is valid for all values to be unspecified - // in which case this AnyValue is considered to be "empty". - oneof value { - string string_value = 1; - bool bool_value = 2; - int64 int_value = 3; - double double_value = 4; - ArrayValue array_value = 5; - KeyValueList kvlist_value = 6; - bytes bytes_value = 7; - } -} - -// ArrayValue is a list of AnyValue messages. We need ArrayValue as a message -// since oneof in AnyValue does not allow repeated fields. -message ArrayValue { - // Array of values. The array may be empty (contain 0 elements). - repeated AnyValue values = 1; -} - -// KeyValueList is a list of KeyValue messages. We need KeyValueList as a message -// since `oneof` in AnyValue does not allow repeated fields. Everywhere else where we need -// a list of KeyValue messages (e.g. in Span) we use `repeated KeyValue` directly to -// avoid unnecessary extra wrapping (which slows down the protocol). The 2 approaches -// are semantically equivalent. -message KeyValueList { - // A collection of key/value pairs of key-value pairs. The list may be empty (may - // contain 0 elements). - repeated KeyValue values = 1; -} - -// KeyValue is a key-value pair that is used to store Span attributes, Link -// attributes, etc. -message KeyValue { - string key = 1; - AnyValue value = 2; -} - -// StringKeyValue is a pair of key/value strings. This is the simpler (and faster) version -// of KeyValue that only supports string values. -message StringKeyValue { - option deprecated = true; - - string key = 1; - string value = 2; -} - -// InstrumentationLibrary is a message representing the instrumentation library information -// such as the fully qualified name and version. -message InstrumentationLibrary { - // An empty instrumentation library name means the name is unknown. - string name = 1; - string version = 2; -} \ No newline at end of file diff --git a/lib/otlp.proto b/lib/otlp.proto deleted file mode 100644 index cee0cf0f..00000000 --- a/lib/otlp.proto +++ /dev/null @@ -1,519 +0,0 @@ -// Copyright 2019, OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package opentelemetry.proto.trace.v1; - -// AnyValue is used to represent any type of attribute value. AnyValue may contain a -// primitive value such as a string or integer or it may contain an arbitrary nested -// object containing arrays, key-value lists and primitives. -message AnyValue { - // The value is one of the listed fields. It is valid for all values to be unspecified - // in which case this AnyValue is considered to be "empty". - oneof value { - string string_value = 1; - bool bool_value = 2; - int64 int_value = 3; - double double_value = 4; - ArrayValue array_value = 5; - KeyValueList kvlist_value = 6; - bytes bytes_value = 7; - } -} - -// ArrayValue is a list of AnyValue messages. We need ArrayValue as a message -// since oneof in AnyValue does not allow repeated fields. -message ArrayValue { - // Array of values. The array may be empty (contain 0 elements). - repeated AnyValue values = 1; -} - -// KeyValueList is a list of KeyValue messages. We need KeyValueList as a message -// since `oneof` in AnyValue does not allow repeated fields. Everywhere else where we need -// a list of KeyValue messages (e.g. in Span) we use `repeated KeyValue` directly to -// avoid unnecessary extra wrapping (which slows down the protocol). The 2 approaches -// are semantically equivalent. -message KeyValueList { - // A collection of key/value pairs of key-value pairs. The list may be empty (may - // contain 0 elements). - // The keys MUST be unique (it is not allowed to have more than one - // value with the same key). - repeated KeyValue values = 1; -} - -// KeyValue is a key-value pair that is used to store Span attributes, Link -// attributes, etc. -message KeyValue { - string key = 1; - AnyValue value = 2; -} - -// InstrumentationScope is a message representing the instrumentation scope information -// such as the fully qualified name and version. -message InstrumentationScope { - // An empty instrumentation scope name means the name is unknown. - string name = 1; - string version = 2; - repeated KeyValue attributes = 3; - uint32 dropped_attributes_count = 4; -} - -message Resource { - // Set of attributes that describe the resource. - // Attribute keys MUST be unique (it is not allowed to have more than one - // attribute with the same key). - repeated KeyValue attributes = 1; - - // dropped_attributes_count is the number of dropped attributes. If the value is 0, then - // no attributes were dropped. - uint32 dropped_attributes_count = 2; -} - -option csharp_namespace = "OpenTelemetry.Proto.Trace.V1"; -option java_multiple_files = true; -option java_package = "io.opentelemetry.proto.trace.v1"; -option java_outer_classname = "TraceProto"; -option go_package = "go.opentelemetry.io/proto/otlp/trace/v1"; - -// TracesData represents the traces data that can be stored in a persistent storage, -// OR can be embedded by other protocols that transfer OTLP traces data but do -// not implement the OTLP protocol. -// -// The main difference between this message and collector protocol is that -// in this message there will not be any "control" or "metadata" specific to -// OTLP protocol. -// -// When new fields are added into this message, the OTLP request MUST be updated -// as well. -message TracesData { - // An array of ResourceSpans. - // For data coming from a single resource this array will typically contain - // one element. Intermediary nodes that receive data from multiple origins - // typically batch the data before forwarding further and in that case this - // array will contain multiple elements. - repeated ResourceSpans resource_spans = 1; -} - -// A collection of ScopeSpans from a Resource. -message ResourceSpans { - reserved 1000; - - // The resource for the spans in this message. - // If this field is not set then no resource info is known. - Resource resource = 1; - - // A list of ScopeSpans that originate from a resource. - repeated ScopeSpans scope_spans = 2; - - // This schema_url applies to the data in the "resource" field. It does not apply - // to the data in the "scope_spans" field which have their own schema_url field. - string schema_url = 3; -} - -// A collection of Spans produced by an InstrumentationScope. -message ScopeSpans { - // The instrumentation scope information for the spans in this message. - // Semantically when InstrumentationScope isn't set, it is equivalent with - // an empty instrumentation scope name (unknown). - InstrumentationScope scope = 1; - - // A list of Spans that originate from an instrumentation scope. - repeated Span spans = 2; - - // This schema_url applies to all spans and span events in the "spans" field. - string schema_url = 3; -} - -// A Span represents a single operation performed by a single component of the system. -// -// The next available field id is 17. -message Span { - // A unique identifier for a trace. All spans from the same trace share - // the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes - // is considered invalid. - // - // This field is semantically required. Receiver should generate new - // random trace_id if empty or invalid trace_id was received. - // - // This field is required. - bytes trace_id = 1; - - // A unique identifier for a span within a trace, assigned when the span - // is created. The ID is an 8-byte array. An ID with all zeroes is considered - // invalid. - // - // This field is semantically required. Receiver should generate new - // random span_id if empty or invalid span_id was received. - // - // This field is required. - bytes span_id = 2; - - // trace_state conveys information about request position in multiple distributed tracing graphs. - // It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header - // See also https://github.com/w3c/distributed-tracing for more details about this field. - string trace_state = 3; - - // The `span_id` of this span's parent span. If this is a root span, then this - // field must be empty. The ID is an 8-byte array. - bytes parent_span_id = 4; - - // A description of the span's operation. - // - // For example, the name can be a qualified method name or a file name - // and a line number where the operation is called. A best practice is to use - // the same display name at the same call point in an application. - // This makes it easier to correlate spans in different traces. - // - // This field is semantically required to be set to non-empty string. - // Empty value is equivalent to an unknown span name. - // - // This field is required. - string name = 5; - - // SpanKind is the type of span. Can be used to specify additional relationships between spans - // in addition to a parent/child relationship. - enum SpanKind { - // Unspecified. Do NOT use as default. - // Implementations MAY assume SpanKind to be INTERNAL when receiving UNSPECIFIED. - SPAN_KIND_UNSPECIFIED = 0; - - // Indicates that the span represents an internal operation within an application, - // as opposed to an operation happening at the boundaries. Default value. - SPAN_KIND_INTERNAL = 1; - - // Indicates that the span covers server-side handling of an RPC or other - // remote network request. - SPAN_KIND_SERVER = 2; - - // Indicates that the span describes a request to some remote service. - SPAN_KIND_CLIENT = 3; - - // Indicates that the span describes a producer sending a message to a broker. - // Unlike CLIENT and SERVER, there is often no direct critical path latency relationship - // between producer and consumer spans. A PRODUCER span ends when the message was accepted - // by the broker while the logical processing of the message might span a much longer time. - SPAN_KIND_PRODUCER = 4; - - // Indicates that the span describes consumer receiving a message from a broker. - // Like the PRODUCER kind, there is often no direct critical path latency relationship - // between producer and consumer spans. - SPAN_KIND_CONSUMER = 5; - } - - // Distinguishes between spans generated in a particular context. For example, - // two spans with the same name may be distinguished using `CLIENT` (caller) - // and `SERVER` (callee) to identify queueing latency associated with the span. - SpanKind kind = 6; - - // start_time_unix_nano is the start time of the span. On the client side, this is the time - // kept by the local machine where the span execution starts. On the server side, this - // is the time when the server's application handler starts running. - // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. - // - // This field is semantically required and it is expected that end_time >= start_time. - fixed64 start_time_unix_nano = 7; - - // end_time_unix_nano is the end time of the span. On the client side, this is the time - // kept by the local machine where the span execution ends. On the server side, this - // is the time when the server application handler stops running. - // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. - // - // This field is semantically required and it is expected that end_time >= start_time. - fixed64 end_time_unix_nano = 8; - - // attributes is a collection of key/value pairs. Note, global attributes - // like server name can be set using the resource API. Examples of attributes: - // - // "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" - // "/http/server_latency": 300 - // "abc.com/myattribute": true - // "abc.com/score": 10.239 - // - // The OpenTelemetry API specification further restricts the allowed value types: - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/README.md#attribute - // Attribute keys MUST be unique (it is not allowed to have more than one - // attribute with the same key). - repeated KeyValue attributes = 9; - - // dropped_attributes_count is the number of attributes that were discarded. Attributes - // can be discarded because their keys are too long or because there are too many - // attributes. If this value is 0, then no attributes were dropped. - uint32 dropped_attributes_count = 10; - - // Event is a time-stamped annotation of the span, consisting of user-supplied - // text description and key-value pairs. - message Event { - // time_unix_nano is the time the event occurred. - fixed64 time_unix_nano = 1; - - // name of the event. - // This field is semantically required to be set to non-empty string. - string name = 2; - - // attributes is a collection of attribute key/value pairs on the event. - // Attribute keys MUST be unique (it is not allowed to have more than one - // attribute with the same key). - repeated KeyValue attributes = 3; - - // dropped_attributes_count is the number of dropped attributes. If the value is 0, - // then no attributes were dropped. - uint32 dropped_attributes_count = 4; - } - - // events is a collection of Event items. - repeated Event events = 11; - - // dropped_events_count is the number of dropped events. If the value is 0, then no - // events were dropped. - uint32 dropped_events_count = 12; - - // A pointer from the current span to another span in the same trace or in a - // different trace. For example, this can be used in batching operations, - // where a single batch handler processes multiple requests from different - // traces or when the handler receives a request from a different project. - message Link { - // A unique identifier of a trace that this linked span is part of. The ID is a - // 16-byte array. - bytes trace_id = 1; - - // A unique identifier for the linked span. The ID is an 8-byte array. - bytes span_id = 2; - - // The trace_state associated with the link. - string trace_state = 3; - - // attributes is a collection of attribute key/value pairs on the link. - // Attribute keys MUST be unique (it is not allowed to have more than one - // attribute with the same key). - repeated KeyValue attributes = 4; - - // dropped_attributes_count is the number of dropped attributes. If the value is 0, - // then no attributes were dropped. - uint32 dropped_attributes_count = 5; - } - - // links is a collection of Links, which are references from this span to a span - // in the same or different trace. - repeated Link links = 13; - - // dropped_links_count is the number of dropped links after the maximum size was - // enforced. If this value is 0, then no links were dropped. - uint32 dropped_links_count = 14; - - // An optional final status for this span. Semantically when Status isn't set, it means - // span's status code is unset, i.e. assume STATUS_CODE_UNSET (code = 0). - Status status = 15; -} - -// The Status type defines a logical error model that is suitable for different -// programming environments, including REST APIs and RPC APIs. -message Status { - reserved 1; - - // A developer-facing human readable error message. - string message = 2; - - // For the semantics of status codes see - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#set-status - enum StatusCode { - // The default status. - STATUS_CODE_UNSET = 0; - // The Span has been validated by an Application developer or Operator to - // have completed successfully. - STATUS_CODE_OK = 1; - // The Span contains an error. - STATUS_CODE_ERROR = 2; - }; - - // The status code. - StatusCode code = 3; -} - -// Recource logs definition - -message LogsData { - // An array of ResourceLogs. - // For data coming from a single resource this array will typically contain - // one element. Intermediary nodes that receive data from multiple origins - // typically batch the data before forwarding further and in that case this - // array will contain multiple elements. - repeated ResourceLogs resource_logs = 1; -} - -// A collection of ScopeLogs from a Resource. -message ResourceLogs { - reserved 1000; - - // The resource for the logs in this message. - // If this field is not set then resource info is unknown. - Resource resource = 1; - - // A list of ScopeLogs that originate from a resource. - repeated ScopeLogs scope_logs = 2; - - // The Schema URL, if known. This is the identifier of the Schema that the resource data - // is recorded in. To learn more about Schema URL see - // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url - // This schema_url applies to the data in the "resource" field. It does not apply - // to the data in the "scope_logs" field which have their own schema_url field. - string schema_url = 3; -} - -// A collection of Logs produced by a Scope. -message ScopeLogs { - // The instrumentation scope information for the logs in this message. - // Semantically when InstrumentationScope isn't set, it is equivalent with - // an empty instrumentation scope name (unknown). - InstrumentationScope scope = 1; - - // A list of log records. - repeated LogRecord log_records = 2; - - // The Schema URL, if known. This is the identifier of the Schema that the log data - // is recorded in. To learn more about Schema URL see - // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url - // This schema_url applies to all logs in the "logs" field. - string schema_url = 3; -} - -// Possible values for LogRecord.SeverityNumber. -enum SeverityNumber { - // UNSPECIFIED is the default SeverityNumber, it MUST NOT be used. - SEVERITY_NUMBER_UNSPECIFIED = 0; - SEVERITY_NUMBER_TRACE = 1; - SEVERITY_NUMBER_TRACE2 = 2; - SEVERITY_NUMBER_TRACE3 = 3; - SEVERITY_NUMBER_TRACE4 = 4; - SEVERITY_NUMBER_DEBUG = 5; - SEVERITY_NUMBER_DEBUG2 = 6; - SEVERITY_NUMBER_DEBUG3 = 7; - SEVERITY_NUMBER_DEBUG4 = 8; - SEVERITY_NUMBER_INFO = 9; - SEVERITY_NUMBER_INFO2 = 10; - SEVERITY_NUMBER_INFO3 = 11; - SEVERITY_NUMBER_INFO4 = 12; - SEVERITY_NUMBER_WARN = 13; - SEVERITY_NUMBER_WARN2 = 14; - SEVERITY_NUMBER_WARN3 = 15; - SEVERITY_NUMBER_WARN4 = 16; - SEVERITY_NUMBER_ERROR = 17; - SEVERITY_NUMBER_ERROR2 = 18; - SEVERITY_NUMBER_ERROR3 = 19; - SEVERITY_NUMBER_ERROR4 = 20; - SEVERITY_NUMBER_FATAL = 21; - SEVERITY_NUMBER_FATAL2 = 22; - SEVERITY_NUMBER_FATAL3 = 23; - SEVERITY_NUMBER_FATAL4 = 24; -} - -// LogRecordFlags represents constants used to interpret the -// LogRecord.flags field, which is protobuf 'fixed32' type and is to -// be used as bit-fields. Each non-zero value defined in this enum is -// a bit-mask. To extract the bit-field, for example, use an -// expression like: -// -// (logRecord.flags & LOG_RECORD_FLAGS_TRACE_FLAGS_MASK) -// -enum LogRecordFlags { - // The zero value for the enum. Should not be used for comparisons. - // Instead use bitwise "and" with the appropriate mask as shown above. - LOG_RECORD_FLAGS_DO_NOT_USE = 0; - - // Bits 0-7 are used for trace flags. - LOG_RECORD_FLAGS_TRACE_FLAGS_MASK = 0x000000FF; - - // Bits 8-31 are reserved for future use. -} - -// A log record according to OpenTelemetry Log Data Model: -// https://github.com/open-telemetry/oteps/blob/main/text/logs/0097-log-data-model.md -message LogRecord { - reserved 4; - - // time_unix_nano is the time when the event occurred. - // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. - // Value of 0 indicates unknown or missing timestamp. - fixed64 time_unix_nano = 1; - - // Time when the event was observed by the collection system. - // For events that originate in OpenTelemetry (e.g. using OpenTelemetry Logging SDK) - // this timestamp is typically set at the generation time and is equal to Timestamp. - // For events originating externally and collected by OpenTelemetry (e.g. using - // Collector) this is the time when OpenTelemetry's code observed the event measured - // by the clock of the OpenTelemetry code. This field MUST be set once the event is - // observed by OpenTelemetry. - // - // For converting OpenTelemetry log data to formats that support only one timestamp or - // when receiving OpenTelemetry log data by recipients that support only one timestamp - // internally the following logic is recommended: - // - Use time_unix_nano if it is present, otherwise use observed_time_unix_nano. - // - // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. - // Value of 0 indicates unknown or missing timestamp. - fixed64 observed_time_unix_nano = 11; - - // Numerical value of the severity, normalized to values described in Log Data Model. - // [Optional]. - SeverityNumber severity_number = 2; - - // The severity text (also known as log level). The original string representation as - // it is known at the source. [Optional]. - string severity_text = 3; - - // A value containing the body of the log record. Can be for example a human-readable - // string message (including multi-line) describing the event in a free form or it can - // be a structured data composed of arrays and maps of other values. [Optional]. - AnyValue body = 5; - - // Additional attributes that describe the specific event occurrence. [Optional]. - // Attribute keys MUST be unique (it is not allowed to have more than one - // attribute with the same key). - repeated KeyValue attributes = 6; - uint32 dropped_attributes_count = 7; - - // Flags, a bit field. 8 least significant bits are the trace flags as - // defined in W3C Trace Context specification. 24 most significant bits are reserved - // and must be set to 0. Readers must not assume that 24 most significant bits - // will be zero and must correctly mask the bits when reading 8-bit trace flag (use - // flags & LOG_RECORD_FLAGS_TRACE_FLAGS_MASK). [Optional]. - fixed32 flags = 8; - - // A unique identifier for a trace. All logs from the same trace share - // the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR - // of length other than 16 bytes is considered invalid (empty string in OTLP/JSON - // is zero-length and thus is also invalid). - // - // This field is optional. - // - // The receivers SHOULD assume that the log record is not associated with a - // trace if any of the following is true: - // - the field is not present, - // - the field contains an invalid value. - bytes trace_id = 9; - - // A unique identifier for a span within a trace, assigned when the span - // is created. The ID is an 8-byte array. An ID with all zeroes OR of length - // other than 8 bytes is considered invalid (empty string in OTLP/JSON - // is zero-length and thus is also invalid). - // - // This field is optional. If the sender specifies a valid span_id then it SHOULD also - // specify a valid trace_id. - // - // The receivers SHOULD assume that the log record is not associated with a - // span if any of the following is true: - // - the field is not present, - // - the field contains an invalid value. - bytes span_id = 10; -} diff --git a/lib/proto/github.com/gogo/protobuf/gogoproto/gogo.proto b/lib/proto/github.com/gogo/protobuf/gogoproto/gogo.proto deleted file mode 100644 index b80c8565..00000000 --- a/lib/proto/github.com/gogo/protobuf/gogoproto/gogo.proto +++ /dev/null @@ -1,144 +0,0 @@ -// Protocol Buffers for Go with Gadgets -// -// Copyright (c) 2013, The GoGo Authors. All rights reserved. -// http://github.com/gogo/protobuf -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -syntax = "proto2"; -package gogoproto; - -import "google/protobuf/descriptor.proto"; - -option java_package = "com.google.protobuf"; -option java_outer_classname = "GoGoProtos"; -option go_package = "github.com/gogo/protobuf/gogoproto"; - -extend google.protobuf.EnumOptions { - optional bool goproto_enum_prefix = 62001; - optional bool goproto_enum_stringer = 62021; - optional bool enum_stringer = 62022; - optional string enum_customname = 62023; - optional bool enumdecl = 62024; -} - -extend google.protobuf.EnumValueOptions { - optional string enumvalue_customname = 66001; -} - -extend google.protobuf.FileOptions { - optional bool goproto_getters_all = 63001; - optional bool goproto_enum_prefix_all = 63002; - optional bool goproto_stringer_all = 63003; - optional bool verbose_equal_all = 63004; - optional bool face_all = 63005; - optional bool gostring_all = 63006; - optional bool populate_all = 63007; - optional bool stringer_all = 63008; - optional bool onlyone_all = 63009; - - optional bool equal_all = 63013; - optional bool description_all = 63014; - optional bool testgen_all = 63015; - optional bool benchgen_all = 63016; - optional bool marshaler_all = 63017; - optional bool unmarshaler_all = 63018; - optional bool stable_marshaler_all = 63019; - - optional bool sizer_all = 63020; - - optional bool goproto_enum_stringer_all = 63021; - optional bool enum_stringer_all = 63022; - - optional bool unsafe_marshaler_all = 63023; - optional bool unsafe_unmarshaler_all = 63024; - - optional bool goproto_extensions_map_all = 63025; - optional bool goproto_unrecognized_all = 63026; - optional bool gogoproto_import = 63027; - optional bool protosizer_all = 63028; - optional bool compare_all = 63029; - optional bool typedecl_all = 63030; - optional bool enumdecl_all = 63031; - - optional bool goproto_registration = 63032; - optional bool messagename_all = 63033; - - optional bool goproto_sizecache_all = 63034; - optional bool goproto_unkeyed_all = 63035; -} - -extend google.protobuf.MessageOptions { - optional bool goproto_getters = 64001; - optional bool goproto_stringer = 64003; - optional bool verbose_equal = 64004; - optional bool face = 64005; - optional bool gostring = 64006; - optional bool populate = 64007; - optional bool stringer = 67008; - optional bool onlyone = 64009; - - optional bool equal = 64013; - optional bool description = 64014; - optional bool testgen = 64015; - optional bool benchgen = 64016; - optional bool marshaler = 64017; - optional bool unmarshaler = 64018; - optional bool stable_marshaler = 64019; - - optional bool sizer = 64020; - - optional bool unsafe_marshaler = 64023; - optional bool unsafe_unmarshaler = 64024; - - optional bool goproto_extensions_map = 64025; - optional bool goproto_unrecognized = 64026; - - optional bool protosizer = 64028; - optional bool compare = 64029; - - optional bool typedecl = 64030; - - optional bool messagename = 64033; - - optional bool goproto_sizecache = 64034; - optional bool goproto_unkeyed = 64035; -} - -extend google.protobuf.FieldOptions { - optional bool nullable = 65001; - optional bool embed = 65002; - optional string customtype = 65003; - optional string customname = 65004; - optional string jsontag = 65005; - optional string moretags = 65006; - optional string casttype = 65007; - optional string castkey = 65008; - optional string castvalue = 65009; - - optional bool stdtime = 65010; - optional bool stdduration = 65011; - optional bool wktpointer = 65012; - -} diff --git a/lib/proto/google/protobuf/descriptor.proto b/lib/proto/google/protobuf/descriptor.proto deleted file mode 100644 index adb57762..00000000 --- a/lib/proto/google/protobuf/descriptor.proto +++ /dev/null @@ -1,885 +0,0 @@ -// Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Author: kenton@google.com (Kenton Varda) -// Based on original Protocol Buffers design by -// Sanjay Ghemawat, Jeff Dean, and others. -// -// The messages in this file describe the definitions found in .proto files. -// A valid .proto file can be translated directly to a FileDescriptorProto -// without any other information (e.g. without reading its imports). - - -syntax = "proto2"; - -package google.protobuf; - -option go_package = "google/protobuf"; -option java_package = "com.google.protobuf"; -option java_outer_classname = "DescriptorProtos"; -option csharp_namespace = "Google.Protobuf.Reflection"; -option objc_class_prefix = "GPB"; -option cc_enable_arenas = true; - -// descriptor.proto must be optimized for speed because reflection-based -// algorithms don't work during bootstrapping. -option optimize_for = SPEED; - -// The protocol compiler can output a FileDescriptorSet containing the .proto -// files it parses. -message FileDescriptorSet { - repeated FileDescriptorProto file = 1; -} - -// Describes a complete .proto file. -message FileDescriptorProto { - optional string name = 1; // file name, relative to root of source tree - optional string package = 2; // e.g. "foo", "foo.bar", etc. - - // Names of files imported by this file. - repeated string dependency = 3; - // Indexes of the public imported files in the dependency list above. - repeated int32 public_dependency = 10; - // Indexes of the weak imported files in the dependency list. - // For Google-internal migration only. Do not use. - repeated int32 weak_dependency = 11; - - // All top-level definitions in this file. - repeated DescriptorProto message_type = 4; - repeated EnumDescriptorProto enum_type = 5; - repeated ServiceDescriptorProto service = 6; - repeated FieldDescriptorProto extension = 7; - - optional FileOptions options = 8; - - // This field contains optional information about the original source code. - // You may safely remove this entire field without harming runtime - // functionality of the descriptors -- the information is needed only by - // development tools. - optional SourceCodeInfo source_code_info = 9; - - // The syntax of the proto file. - // The supported values are "proto2" and "proto3". - optional string syntax = 12; -} - -// Describes a message type. -message DescriptorProto { - optional string name = 1; - - repeated FieldDescriptorProto field = 2; - repeated FieldDescriptorProto extension = 6; - - repeated DescriptorProto nested_type = 3; - repeated EnumDescriptorProto enum_type = 4; - - message ExtensionRange { - optional int32 start = 1; // Inclusive. - optional int32 end = 2; // Exclusive. - - optional ExtensionRangeOptions options = 3; - } - repeated ExtensionRange extension_range = 5; - - repeated OneofDescriptorProto oneof_decl = 8; - - optional MessageOptions options = 7; - - // Range of reserved tag numbers. Reserved tag numbers may not be used by - // fields or extension ranges in the same message. Reserved ranges may - // not overlap. - message ReservedRange { - optional int32 start = 1; // Inclusive. - optional int32 end = 2; // Exclusive. - } - repeated ReservedRange reserved_range = 9; - // Reserved field names, which may not be used by fields in the same message. - // A given name may only be reserved once. - repeated string reserved_name = 10; -} - -message ExtensionRangeOptions { - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -// Describes a field within a message. -message FieldDescriptorProto { - enum Type { - // 0 is reserved for errors. - // Order is weird for historical reasons. - TYPE_DOUBLE = 1; - TYPE_FLOAT = 2; - // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if - // negative values are likely. - TYPE_INT64 = 3; - TYPE_UINT64 = 4; - // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if - // negative values are likely. - TYPE_INT32 = 5; - TYPE_FIXED64 = 6; - TYPE_FIXED32 = 7; - TYPE_BOOL = 8; - TYPE_STRING = 9; - // Tag-delimited aggregate. - // Group type is deprecated and not supported in proto3. However, Proto3 - // implementations should still be able to parse the group wire format and - // treat group fields as unknown fields. - TYPE_GROUP = 10; - TYPE_MESSAGE = 11; // Length-delimited aggregate. - - // New in version 2. - TYPE_BYTES = 12; - TYPE_UINT32 = 13; - TYPE_ENUM = 14; - TYPE_SFIXED32 = 15; - TYPE_SFIXED64 = 16; - TYPE_SINT32 = 17; // Uses ZigZag encoding. - TYPE_SINT64 = 18; // Uses ZigZag encoding. - } - - enum Label { - // 0 is reserved for errors - LABEL_OPTIONAL = 1; - LABEL_REQUIRED = 2; - LABEL_REPEATED = 3; - } - - optional string name = 1; - optional int32 number = 3; - optional Label label = 4; - - // If type_name is set, this need not be set. If both this and type_name - // are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP. - optional Type type = 5; - - // For message and enum types, this is the name of the type. If the name - // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping - // rules are used to find the type (i.e. first the nested types within this - // message are searched, then within the parent, on up to the root - // namespace). - optional string type_name = 6; - - // For extensions, this is the name of the type being extended. It is - // resolved in the same manner as type_name. - optional string extendee = 2; - - // For numeric types, contains the original text representation of the value. - // For booleans, "true" or "false". - // For strings, contains the default text contents (not escaped in any way). - // For bytes, contains the C escaped value. All bytes >= 128 are escaped. - // TODO(kenton): Base-64 encode? - optional string default_value = 7; - - // If set, gives the index of a oneof in the containing type's oneof_decl - // list. This field is a member of that oneof. - optional int32 oneof_index = 9; - - // JSON name of this field. The value is set by protocol compiler. If the - // user has set a "json_name" option on this field, that option's value - // will be used. Otherwise, it's deduced from the field's name by converting - // it to camelCase. - optional string json_name = 10; - - optional FieldOptions options = 8; -} - -// Describes a oneof. -message OneofDescriptorProto { - optional string name = 1; - optional OneofOptions options = 2; -} - -// Describes an enum type. -message EnumDescriptorProto { - optional string name = 1; - - repeated EnumValueDescriptorProto value = 2; - - optional EnumOptions options = 3; - - // Range of reserved numeric values. Reserved values may not be used by - // entries in the same enum. Reserved ranges may not overlap. - // - // Note that this is distinct from DescriptorProto.ReservedRange in that it - // is inclusive such that it can appropriately represent the entire int32 - // domain. - message EnumReservedRange { - optional int32 start = 1; // Inclusive. - optional int32 end = 2; // Inclusive. - } - - // Range of reserved numeric values. Reserved numeric values may not be used - // by enum values in the same enum declaration. Reserved ranges may not - // overlap. - repeated EnumReservedRange reserved_range = 4; - - // Reserved enum value names, which may not be reused. A given name may only - // be reserved once. - repeated string reserved_name = 5; -} - -// Describes a value within an enum. -message EnumValueDescriptorProto { - optional string name = 1; - optional int32 number = 2; - - optional EnumValueOptions options = 3; -} - -// Describes a service. -message ServiceDescriptorProto { - optional string name = 1; - repeated MethodDescriptorProto method = 2; - - optional ServiceOptions options = 3; -} - -// Describes a method of a service. -message MethodDescriptorProto { - optional string name = 1; - - // Input and output type names. These are resolved in the same way as - // FieldDescriptorProto.type_name, but must refer to a message type. - optional string input_type = 2; - optional string output_type = 3; - - optional MethodOptions options = 4; - - // Identifies if client streams multiple client messages - optional bool client_streaming = 5 [default = false]; - // Identifies if server streams multiple server messages - optional bool server_streaming = 6 [default = false]; -} - - -// =================================================================== -// Options - -// Each of the definitions above may have "options" attached. These are -// just annotations which may cause code to be generated slightly differently -// or may contain hints for code that manipulates protocol messages. -// -// Clients may define custom options as extensions of the *Options messages. -// These extensions may not yet be known at parsing time, so the parser cannot -// store the values in them. Instead it stores them in a field in the *Options -// message called uninterpreted_option. This field must have the same name -// across all *Options messages. We then use this field to populate the -// extensions when we build a descriptor, at which point all protos have been -// parsed and so all extensions are known. -// -// Extension numbers for custom options may be chosen as follows: -// * For options which will only be used within a single application or -// organization, or for experimental options, use field numbers 50000 -// through 99999. It is up to you to ensure that you do not use the -// same number for multiple options. -// * For options which will be published and used publicly by multiple -// independent entities, e-mail protobuf-global-extension-registry@google.com -// to reserve extension numbers. Simply provide your project name (e.g. -// Objective-C plugin) and your project website (if available) -- there's no -// need to explain how you intend to use them. Usually you only need one -// extension number. You can declare multiple options with only one extension -// number by putting them in a sub-message. See the Custom Options section of -// the docs for examples: -// https://developers.google.com/protocol-buffers/docs/proto#options -// If this turns out to be popular, a web service will be set up -// to automatically assign option numbers. - -message FileOptions { - - // Sets the Java package where classes generated from this .proto will be - // placed. By default, the proto package is used, but this is often - // inappropriate because proto packages do not normally start with backwards - // domain names. - optional string java_package = 1; - - - // If set, all the classes from the .proto file are wrapped in a single - // outer class with the given name. This applies to both Proto1 - // (equivalent to the old "--one_java_file" option) and Proto2 (where - // a .proto always translates to a single class, but you may want to - // explicitly choose the class name). - optional string java_outer_classname = 8; - - // If set true, then the Java code generator will generate a separate .java - // file for each top-level message, enum, and service defined in the .proto - // file. Thus, these types will *not* be nested inside the outer class - // named by java_outer_classname. However, the outer class will still be - // generated to contain the file's getDescriptor() method as well as any - // top-level extensions defined in the file. - optional bool java_multiple_files = 10 [default = false]; - - // This option does nothing. - optional bool java_generate_equals_and_hash = 20 [deprecated=true]; - - // If set true, then the Java2 code generator will generate code that - // throws an exception whenever an attempt is made to assign a non-UTF-8 - // byte sequence to a string field. - // Message reflection will do the same. - // However, an extension field still accepts non-UTF-8 byte sequences. - // This option has no effect on when used with the lite runtime. - optional bool java_string_check_utf8 = 27 [default = false]; - - - // Generated classes can be optimized for speed or code size. - enum OptimizeMode { - SPEED = 1; // Generate complete code for parsing, serialization, - // etc. - CODE_SIZE = 2; // Use ReflectionOps to implement these methods. - LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime. - } - optional OptimizeMode optimize_for = 9 [default = SPEED]; - - // Sets the Go package where structs generated from this .proto will be - // placed. If omitted, the Go package will be derived from the following: - // - The basename of the package import path, if provided. - // - Otherwise, the package statement in the .proto file, if present. - // - Otherwise, the basename of the .proto file, without extension. - optional string go_package = 11; - - - - - // Should generic services be generated in each language? "Generic" services - // are not specific to any particular RPC system. They are generated by the - // main code generators in each language (without additional plugins). - // Generic services were the only kind of service generation supported by - // early versions of google.protobuf. - // - // Generic services are now considered deprecated in favor of using plugins - // that generate code specific to your particular RPC system. Therefore, - // these default to false. Old code which depends on generic services should - // explicitly set them to true. - optional bool cc_generic_services = 16 [default = false]; - optional bool java_generic_services = 17 [default = false]; - optional bool py_generic_services = 18 [default = false]; - optional bool php_generic_services = 42 [default = false]; - - // Is this file deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for everything in the file, or it will be completely ignored; in the very - // least, this is a formalization for deprecating files. - optional bool deprecated = 23 [default = false]; - - // Enables the use of arenas for the proto messages in this file. This applies - // only to generated classes for C++. - optional bool cc_enable_arenas = 31 [default = false]; - - - // Sets the objective c class prefix which is prepended to all objective c - // generated classes from this .proto. There is no default. - optional string objc_class_prefix = 36; - - // Namespace for generated classes; defaults to the package. - optional string csharp_namespace = 37; - - // By default Swift generators will take the proto package and CamelCase it - // replacing '.' with underscore and use that to prefix the types/symbols - // defined. When this options is provided, they will use this value instead - // to prefix the types/symbols defined. - optional string swift_prefix = 39; - - // Sets the php class prefix which is prepended to all php generated classes - // from this .proto. Default is empty. - optional string php_class_prefix = 40; - - // Use this option to change the namespace of php generated classes. Default - // is empty. When this option is empty, the package name will be used for - // determining the namespace. - optional string php_namespace = 41; - - // Use this option to change the namespace of php generated metadata classes. - // Default is empty. When this option is empty, the proto file name will be - // used for determining the namespace. - optional string php_metadata_namespace = 44; - - // Use this option to change the package of ruby generated classes. Default - // is empty. When this option is not set, the package name will be used for - // determining the ruby package. - optional string ruby_package = 45; - - - // The parser stores options it doesn't recognize here. - // See the documentation for the "Options" section above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. - // See the documentation for the "Options" section above. - extensions 1000 to max; - - //reserved 38; -} - -message MessageOptions { - // Set true to use the old proto1 MessageSet wire format for extensions. - // This is provided for backwards-compatibility with the MessageSet wire - // format. You should not use this for any other reason: It's less - // efficient, has fewer features, and is more complicated. - // - // The message must be defined exactly as follows: - // message Foo { - // option message_set_wire_format = true; - // extensions 4 to max; - // } - // Note that the message cannot have any defined fields; MessageSets only - // have extensions. - // - // All extensions of your type must be singular messages; e.g. they cannot - // be int32s, enums, or repeated messages. - // - // Because this is an option, the above two restrictions are not enforced by - // the protocol compiler. - optional bool message_set_wire_format = 1 [default = false]; - - // Disables the generation of the standard "descriptor()" accessor, which can - // conflict with a field of the same name. This is meant to make migration - // from proto1 easier; new code should avoid fields named "descriptor". - optional bool no_standard_descriptor_accessor = 2 [default = false]; - - // Is this message deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the message, or it will be completely ignored; in the very least, - // this is a formalization for deprecating messages. - optional bool deprecated = 3 [default = false]; - - // Whether the message is an automatically generated map entry type for the - // maps field. - // - // For maps fields: - // map<KeyType, ValueType> map_field = 1; - // The parsed descriptor looks like: - // message MapFieldEntry { - // option map_entry = true; - // optional KeyType key = 1; - // optional ValueType value = 2; - // } - // repeated MapFieldEntry map_field = 1; - // - // Implementations may choose not to generate the map_entry=true message, but - // use a native map in the target language to hold the keys and values. - // The reflection APIs in such implementations still need to work as - // if the field is a repeated message field. - // - // NOTE: Do not set the option in .proto files. Always use the maps syntax - // instead. The option should only be implicitly set by the proto compiler - // parser. - optional bool map_entry = 7; - - //reserved 8; // javalite_serializable - //reserved 9; // javanano_as_lite - - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message FieldOptions { - // The ctype option instructs the C++ code generator to use a different - // representation of the field than it normally would. See the specific - // options below. This option is not yet implemented in the open source - // release -- sorry, we'll try to include it in a future version! - optional CType ctype = 1 [default = STRING]; - enum CType { - // Default mode. - STRING = 0; - - CORD = 1; - - STRING_PIECE = 2; - } - // The packed option can be enabled for repeated primitive fields to enable - // a more efficient representation on the wire. Rather than repeatedly - // writing the tag and type for each element, the entire array is encoded as - // a single length-delimited blob. In proto3, only explicit setting it to - // false will avoid using packed encoding. - optional bool packed = 2; - - // The jstype option determines the JavaScript type used for values of the - // field. The option is permitted only for 64 bit integral and fixed types - // (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING - // is represented as JavaScript string, which avoids loss of precision that - // can happen when a large value is converted to a floating point JavaScript. - // Specifying JS_NUMBER for the jstype causes the generated JavaScript code to - // use the JavaScript "number" type. The behavior of the default option - // JS_NORMAL is implementation dependent. - // - // This option is an enum to permit additional types to be added, e.g. - // goog.math.Integer. - optional JSType jstype = 6 [default = JS_NORMAL]; - enum JSType { - // Use the default type. - JS_NORMAL = 0; - - // Use JavaScript strings. - JS_STRING = 1; - - // Use JavaScript numbers. - JS_NUMBER = 2; - } - - // Should this field be parsed lazily? Lazy applies only to message-type - // fields. It means that when the outer message is initially parsed, the - // inner message's contents will not be parsed but instead stored in encoded - // form. The inner message will actually be parsed when it is first accessed. - // - // This is only a hint. Implementations are free to choose whether to use - // eager or lazy parsing regardless of the value of this option. However, - // setting this option true suggests that the protocol author believes that - // using lazy parsing on this field is worth the additional bookkeeping - // overhead typically needed to implement it. - // - // This option does not affect the public interface of any generated code; - // all method signatures remain the same. Furthermore, thread-safety of the - // interface is not affected by this option; const methods remain safe to - // call from multiple threads concurrently, while non-const methods continue - // to require exclusive access. - // - // - // Note that implementations may choose not to check required fields within - // a lazy sub-message. That is, calling IsInitialized() on the outer message - // may return true even if the inner message has missing required fields. - // This is necessary because otherwise the inner message would have to be - // parsed in order to perform the check, defeating the purpose of lazy - // parsing. An implementation which chooses not to check required fields - // must be consistent about it. That is, for any particular sub-message, the - // implementation must either *always* check its required fields, or *never* - // check its required fields, regardless of whether or not the message has - // been parsed. - optional bool lazy = 5 [default = false]; - - // Is this field deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for accessors, or it will be completely ignored; in the very least, this - // is a formalization for deprecating fields. - optional bool deprecated = 3 [default = false]; - - // For Google-internal migration only. Do not use. - optional bool weak = 10 [default = false]; - - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; - - //reserved 4; // removed jtype -} - -message OneofOptions { - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message EnumOptions { - - // Set this option to true to allow mapping different tag names to the same - // value. - optional bool allow_alias = 2; - - // Is this enum deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the enum, or it will be completely ignored; in the very least, this - // is a formalization for deprecating enums. - optional bool deprecated = 3 [default = false]; - - //reserved 5; // javanano_as_lite - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message EnumValueOptions { - // Is this enum value deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the enum value, or it will be completely ignored; in the very least, - // this is a formalization for deprecating enum values. - optional bool deprecated = 1 [default = false]; - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message ServiceOptions { - - // Note: Field numbers 1 through 32 are reserved for Google's internal RPC - // framework. We apologize for hoarding these numbers to ourselves, but - // we were already using them long before we decided to release Protocol - // Buffers. - - // Is this service deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the service, or it will be completely ignored; in the very least, - // this is a formalization for deprecating services. - optional bool deprecated = 33 [default = false]; - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message MethodOptions { - - // Note: Field numbers 1 through 32 are reserved for Google's internal RPC - // framework. We apologize for hoarding these numbers to ourselves, but - // we were already using them long before we decided to release Protocol - // Buffers. - - // Is this method deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the method, or it will be completely ignored; in the very least, - // this is a formalization for deprecating methods. - optional bool deprecated = 33 [default = false]; - - // Is this method side-effect-free (or safe in HTTP parlance), or idempotent, - // or neither? HTTP based RPC implementation may choose GET verb for safe - // methods, and PUT verb for idempotent methods instead of the default POST. - enum IdempotencyLevel { - IDEMPOTENCY_UNKNOWN = 0; - NO_SIDE_EFFECTS = 1; // implies idempotent - IDEMPOTENT = 2; // idempotent, but may have side effects - } - optional IdempotencyLevel idempotency_level = 34 - [default = IDEMPOTENCY_UNKNOWN]; - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - - -// A message representing a option the parser does not recognize. This only -// appears in options protos created by the compiler::Parser class. -// DescriptorPool resolves these when building Descriptor objects. Therefore, -// options protos in descriptor objects (e.g. returned by Descriptor::options(), -// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions -// in them. -message UninterpretedOption { - // The name of the uninterpreted option. Each string represents a segment in - // a dot-separated name. is_extension is true iff a segment represents an - // extension (denoted with parentheses in options specs in .proto files). - // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents - // "foo.(bar.baz).qux". - message NamePart { - required string name_part = 1; - required bool is_extension = 2; - } - repeated NamePart name = 2; - - // The value of the uninterpreted option, in whatever type the tokenizer - // identified it as during parsing. Exactly one of these should be set. - optional string identifier_value = 3; - optional uint64 positive_int_value = 4; - optional int64 negative_int_value = 5; - optional double double_value = 6; - optional bytes string_value = 7; - optional string aggregate_value = 8; -} - -// =================================================================== -// Optional source code info - -// Encapsulates information about the original source file from which a -// FileDescriptorProto was generated. -message SourceCodeInfo { - // A Location identifies a piece of source code in a .proto file which - // corresponds to a particular definition. This information is intended - // to be useful to IDEs, code indexers, documentation generators, and similar - // tools. - // - // For example, say we have a file like: - // message Foo { - // optional string foo = 1; - // } - // Let's look at just the field definition: - // optional string foo = 1; - // ^ ^^ ^^ ^ ^^^ - // a bc de f ghi - // We have the following locations: - // span path represents - // [a,i) [ 4, 0, 2, 0 ] The whole field definition. - // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional). - // [c,d) [ 4, 0, 2, 0, 5 ] The type (string). - // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo). - // [g,h) [ 4, 0, 2, 0, 3 ] The number (1). - // - // Notes: - // - A location may refer to a repeated field itself (i.e. not to any - // particular index within it). This is used whenever a set of elements are - // logically enclosed in a single code segment. For example, an entire - // extend block (possibly containing multiple extension definitions) will - // have an outer location whose path refers to the "extensions" repeated - // field without an index. - // - Multiple locations may have the same path. This happens when a single - // logical declaration is spread out across multiple places. The most - // obvious example is the "extend" block again -- there may be multiple - // extend blocks in the same scope, each of which will have the same path. - // - A location's span is not always a subset of its parent's span. For - // example, the "extendee" of an extension declaration appears at the - // beginning of the "extend" block and is shared by all extensions within - // the block. - // - Just because a location's span is a subset of some other location's span - // does not mean that it is a descendant. For example, a "group" defines - // both a type and a field in a single declaration. Thus, the locations - // corresponding to the type and field and their components will overlap. - // - Code which tries to interpret locations should probably be designed to - // ignore those that it doesn't understand, as more types of locations could - // be recorded in the future. - repeated Location location = 1; - message Location { - // Identifies which part of the FileDescriptorProto was defined at this - // location. - // - // Each element is a field number or an index. They form a path from - // the root FileDescriptorProto to the place where the definition. For - // example, this path: - // [ 4, 3, 2, 7, 1 ] - // refers to: - // file.message_type(3) // 4, 3 - // .field(7) // 2, 7 - // .name() // 1 - // This is because FileDescriptorProto.message_type has field number 4: - // repeated DescriptorProto message_type = 4; - // and DescriptorProto.field has field number 2: - // repeated FieldDescriptorProto field = 2; - // and FieldDescriptorProto.name has field number 1: - // optional string name = 1; - // - // Thus, the above path gives the location of a field name. If we removed - // the last element: - // [ 4, 3, 2, 7 ] - // this path refers to the whole field declaration (from the beginning - // of the label to the terminating semicolon). - repeated int32 path = 1 [packed = true]; - - // Always has exactly three or four elements: start line, start column, - // end line (optional, otherwise assumed same as start line), end column. - // These are packed into a single field for efficiency. Note that line - // and column numbers are zero-based -- typically you will want to add - // 1 to each before displaying to a user. - repeated int32 span = 2 [packed = true]; - - // If this SourceCodeInfo represents a complete declaration, these are any - // comments appearing before and after the declaration which appear to be - // attached to the declaration. - // - // A series of line comments appearing on consecutive lines, with no other - // tokens appearing on those lines, will be treated as a single comment. - // - // leading_detached_comments will keep paragraphs of comments that appear - // before (but not connected to) the current element. Each paragraph, - // separated by empty lines, will be one comment element in the repeated - // field. - // - // Only the comment content is provided; comment markers (e.g. //) are - // stripped out. For block comments, leading whitespace and an asterisk - // will be stripped from the beginning of each line other than the first. - // Newlines are included in the output. - // - // Examples: - // - // optional int32 foo = 1; // Comment attached to foo. - // // Comment attached to bar. - // optional int32 bar = 2; - // - // optional string baz = 3; - // // Comment attached to baz. - // // Another line attached to baz. - // - // // Comment attached to qux. - // // - // // Another line attached to qux. - // optional double qux = 4; - // - // // Detached comment for corge. This is not leading or trailing comments - // // to qux or corge because there are blank lines separating it from - // // both. - // - // // Detached comment for corge paragraph 2. - // - // optional string corge = 5; - // /* Block comment attached - // * to corge. Leading asterisks - // * will be removed. */ - // /* Block comment attached to - // * grault. */ - // optional int32 grault = 6; - // - // // ignored detached comments. - optional string leading_comments = 3; - optional string trailing_comments = 4; - repeated string leading_detached_comments = 6; - } -} - -// Describes the relationship between generated code and its original source -// file. A GeneratedCodeInfo message is associated with only one generated -// source file, but may contain references to different source .proto files. -message GeneratedCodeInfo { - // An Annotation connects some span of text in generated code to an element - // of its generating .proto file. - repeated Annotation annotation = 1; - message Annotation { - // Identifies the element in the original source .proto file. This field - // is formatted the same as SourceCodeInfo.Location.path. - repeated int32 path = 1 [packed = true]; - - // Identifies the filesystem path to the original source .proto. - optional string source_file = 2; - - // Identifies the starting offset in bytes in the generated code - // that relates to the identified object. - optional int32 begin = 3; - - // Identifies the ending offset in bytes in the generated code that - // relates to the identified offset. The end offset should be one past - // the last relevant byte (so the length of the text = end - begin). - optional int32 end = 4; - } -} diff --git a/lib/proto/google/protobuf/timestamp.proto b/lib/proto/google/protobuf/timestamp.proto deleted file mode 100644 index 028c7d89..00000000 --- a/lib/proto/google/protobuf/timestamp.proto +++ /dev/null @@ -1,138 +0,0 @@ -// Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -syntax = "proto3"; - -package google.protobuf; - -option csharp_namespace = "Google.Protobuf.WellKnownTypes"; -option cc_enable_arenas = true; -option go_package = "google/protobuf"; -option java_package = "com.google.protobuf"; -option java_outer_classname = "TimestampProto"; -option java_multiple_files = true; -option objc_class_prefix = "GPB"; - -// A Timestamp represents a point in time independent of any time zone or local -// calendar, encoded as a count of seconds and fractions of seconds at -// nanosecond resolution. The count is relative to an epoch at UTC midnight on -// January 1, 1970, in the proleptic Gregorian calendar which extends the -// Gregorian calendar backwards to year one. -// -// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap -// second table is needed for interpretation, using a [24-hour linear -// smear](https://developers.google.com/time/smear). -// -// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By -// restricting to that range, we ensure that we can convert to and from [RFC -// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. -// -// # Examples -// -// Example 1: Compute Timestamp from POSIX `time()`. -// -// Timestamp timestamp; -// timestamp.set_seconds(time(NULL)); -// timestamp.set_nanos(0); -// -// Example 2: Compute Timestamp from POSIX `gettimeofday()`. -// -// struct timeval tv; -// gettimeofday(&tv, NULL); -// -// Timestamp timestamp; -// timestamp.set_seconds(tv.tv_sec); -// timestamp.set_nanos(tv.tv_usec * 1000); -// -// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. -// -// FILETIME ft; -// GetSystemTimeAsFileTime(&ft); -// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; -// -// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z -// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. -// Timestamp timestamp; -// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); -// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); -// -// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. -// -// long millis = System.currentTimeMillis(); -// -// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) -// .setNanos((int) ((millis % 1000) * 1000000)).build(); -// -// -// Example 5: Compute Timestamp from current time in Python. -// -// timestamp = Timestamp() -// timestamp.GetCurrentTime() -// -// # JSON Mapping -// -// In JSON format, the Timestamp type is encoded as a string in the -// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the -// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" -// where {year} is always expressed using four digits while {month}, {day}, -// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional -// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), -// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone -// is required. A proto3 JSON serializer should always use UTC (as indicated by -// "Z") when printing the Timestamp type and a proto3 JSON parser should be -// able to accept both UTC and other timezones (as indicated by an offset). -// -// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past -// 01:30 UTC on January 15, 2017. -// -// In JavaScript, one can convert a Date object to this format using the -// standard -// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) -// method. In Python, a standard `datetime.datetime` object can be converted -// to this format using -// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with -// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use -// the Joda Time's [`ISODateTimeFormat.dateTime()`]( -// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D -// ) to obtain a formatter capable of generating timestamps in this format. -// -// -message Timestamp { - // Represents seconds of UTC time since Unix epoch - // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to - // 9999-12-31T23:59:59Z inclusive. - int64 seconds = 1; - - // Non-negative fractions of a second at nanosecond resolution. Negative - // second values with fractions must still have non-negative nanos values - // that count forward in time. Must be from 0 to 999,999,999 - // inclusive. - int32 nanos = 2; -} diff --git a/lib/proto/logproto.proto b/lib/proto/logproto.proto deleted file mode 100644 index e54a22c2..00000000 --- a/lib/proto/logproto.proto +++ /dev/null @@ -1,167 +0,0 @@ -syntax = "proto3"; - -package logproto; - -option go_package = "github.com/grafana/loki/pkg/logproto"; - -import "google/protobuf/timestamp.proto"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -service Pusher { - rpc Push(PushRequest) returns (PushResponse) {}; -} - -service Querier { - rpc Query(QueryRequest) returns (stream QueryResponse) {}; - rpc QuerySample(SampleQueryRequest) returns (stream SampleQueryResponse) {}; - rpc Label(LabelRequest) returns (LabelResponse) {}; - rpc Tail(TailRequest) returns (stream TailResponse) {}; - rpc Series(SeriesRequest) returns (SeriesResponse) {}; - rpc TailersCount(TailersCountRequest) returns (TailersCountResponse) {}; - rpc GetChunkIDs(GetChunkIDsRequest) returns (GetChunkIDsResponse) {}; // GetChunkIDs returns ChunkIDs from the index store holding logs for given selectors and time-range. -} - -service Ingester { - rpc TransferChunks(stream TimeSeriesChunk) returns (TransferChunksResponse) {}; -} - -message PushRequest { - repeated StreamAdapter streams = 1 [(gogoproto.jsontag) = "streams", (gogoproto.customtype) = "Stream"]; -} - -message PushResponse { -} - -message QueryRequest { - string selector = 1; - uint32 limit = 2; - google.protobuf.Timestamp start = 3 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - google.protobuf.Timestamp end = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - Direction direction = 5; - reserved 6; - repeated string shards = 7 [(gogoproto.jsontag) = "shards,omitempty"]; -} - -message SampleQueryRequest { - string selector = 1; - google.protobuf.Timestamp start = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - google.protobuf.Timestamp end = 3 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - repeated string shards = 4 [(gogoproto.jsontag) = "shards,omitempty"]; -} - -message SampleQueryResponse { - repeated Series series = 1 [(gogoproto.customtype) = "Series", (gogoproto.nullable) = true]; -} - -enum Direction { - FORWARD = 0; - BACKWARD = 1; -} - -message QueryResponse { - repeated StreamAdapter streams = 1 [(gogoproto.customtype) = "Stream", (gogoproto.nullable) = true]; -} - -message LabelRequest { - string name = 1; - bool values = 2; // True to fetch label values, false for fetch labels names. - google.protobuf.Timestamp start = 3 [(gogoproto.stdtime) = true, (gogoproto.nullable) = true]; - google.protobuf.Timestamp end = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = true]; -} - -message LabelResponse { - repeated string values = 1; -} - -message StreamAdapter { - string labels = 1 [(gogoproto.jsontag) = "labels"]; - repeated EntryAdapter entries = 2 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "entries"]; -} - -message EntryAdapter { - google.protobuf.Timestamp timestamp = 1 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (gogoproto.jsontag) = "ts"]; - string line = 2 [(gogoproto.jsontag) = "line"]; -} - -message Sample { - int64 timestamp = 1 [(gogoproto.jsontag) = "ts"]; - double value = 2 [(gogoproto.jsontag) = "value"]; - uint64 hash = 3 [(gogoproto.jsontag) = "hash"]; -} - -message Series { - string labels = 1 [(gogoproto.jsontag) = "labels"]; - repeated Sample samples = 2 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "samples"]; -} - -message TailRequest { - string query = 1; - reserved 2; - uint32 delayFor = 3; - uint32 limit = 4; - google.protobuf.Timestamp start = 5 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; -} - -message TailResponse { - StreamAdapter stream = 1 [(gogoproto.customtype) = "Stream"]; - repeated DroppedStream droppedStreams = 2; -} - -message SeriesRequest { - google.protobuf.Timestamp start = 1 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - google.protobuf.Timestamp end = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - repeated string groups = 3; - repeated string shards = 4 [(gogoproto.jsontag) = "shards,omitempty"]; -} - -message SeriesResponse { - repeated SeriesIdentifier series = 1 [(gogoproto.nullable) = false]; -} - -message SeriesIdentifier { - map<string,string> labels = 1; -} - -message DroppedStream { - google.protobuf.Timestamp from = 1 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - google.protobuf.Timestamp to = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - string labels = 3; -} - -message TimeSeriesChunk { - string from_ingester_id = 1; - string user_id = 2; - repeated LabelPair labels = 3; - repeated Chunk chunks = 4; -} - -message LabelPair { - string name = 1; - string value = 2; -} - -message Chunk { - bytes data = 1; -} - -message TransferChunksResponse { - -} - -message TailersCountRequest { - -} - -message TailersCountResponse { - uint32 count = 1; -} - -message GetChunkIDsRequest { - string matchers = 1; - google.protobuf.Timestamp start = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - google.protobuf.Timestamp end = 3 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; -} - -message GetChunkIDsResponse { - repeated string chunkIDs = 1; -} diff --git a/lib/tempo.proto b/lib/tempo.proto deleted file mode 100644 index afc469d0..00000000 --- a/lib/tempo.proto +++ /dev/null @@ -1,52 +0,0 @@ -syntax="proto3"; - -package tempopb; - -message TraceByIDResponse { - Trace trace = 1; - TraceByIDMetrics metrics = 2; -} - -message TraceByIDMetrics { - uint32 failedBlocks = 1; -} - -message SearchResponse { - repeated TraceSearchMetadata traces = 1; - SearchMetrics metrics = 2; -} - -message TraceSearchMetadata { - string traceID = 1; - string rootServiceName = 2; - string rootTraceName = 3; - uint64 startTimeUnixNano = 4; - uint32 durationMs = 5; -} - -message SearchMetrics { - uint32 inspectedTraces = 1; - uint64 inspectedBytes = 2; - uint32 inspectedBlocks = 3; - uint32 skippedBlocks = 4; -} - -message SearchTagsRequest { -} - -message SearchTagsResponse { - repeated string tagNames = 1; -} - -message SearchTagValuesRequest { - string tagName = 1; -} - -message SearchTagValuesResponse { - repeated string tagValues = 1; -} - -message TraceBytes { - // pre-marshalled Traces - repeated bytes traces = 1; -} diff --git a/lib/timestamp.proto b/lib/timestamp.proto deleted file mode 100644 index c8f72887..00000000 --- a/lib/timestamp.proto +++ /dev/null @@ -1,107 +0,0 @@ -syntax = "proto3"; - -package google.protobuf; - -option csharp_namespace = "Google.Protobuf.WellKnownTypes"; -option cc_enable_arenas = true; -option go_package = "github.com/golang/protobuf/ptypes/timestamp"; -option java_package = "com.google.protobuf"; -option java_outer_classname = "TimestampProto"; -option java_multiple_files = true; -option objc_class_prefix = "GPB"; - -// A Timestamp represents a point in time independent of any time zone or local -// calendar, encoded as a count of seconds and fractions of seconds at -// nanosecond resolution. The count is relative to an epoch at UTC midnight on -// January 1, 1970, in the proleptic Gregorian calendar which extends the -// Gregorian calendar backwards to year one. -// -// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap -// second table is needed for interpretation, using a [24-hour linear -// smear](https://developers.google.com/time/smear). -// -// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By -// restricting to that range, we ensure that we can convert to and from [RFC -// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. -// -// # Examples -// -// Example 1: Compute Timestamp from POSIX `time()`. -// -// Timestamp timestamp; -// timestamp.set_seconds(time(NULL)); -// timestamp.set_nanos(0); -// -// Example 2: Compute Timestamp from POSIX `gettimeofday()`. -// -// struct timeval tv; -// gettimeofday(&tv, NULL); -// -// Timestamp timestamp; -// timestamp.set_seconds(tv.tv_sec); -// timestamp.set_nanos(tv.tv_usec * 1000); -// -// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. -// -// FILETIME ft; -// GetSystemTimeAsFileTime(&ft); -// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; -// -// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z -// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. -// Timestamp timestamp; -// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); -// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); -// -// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. -// -// long millis = System.currentTimeMillis(); -// -// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) -// .setNanos((int) ((millis % 1000) * 1000000)).build(); -// -// -// Example 5: Compute Timestamp from current time in Python. -// -// timestamp = Timestamp() -// timestamp.GetCurrentTime() -// -// # JSON Mapping -// -// In JSON format, the Timestamp type is encoded as a string in the -// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the -// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" -// where {year} is always expressed using four digits while {month}, {day}, -// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional -// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), -// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone -// is required. A proto3 JSON serializer should always use UTC (as indicated by -// "Z") when printing the Timestamp type and a proto3 JSON parser should be -// able to accept both UTC and other timezones (as indicated by an offset). -// -// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past -// 01:30 UTC on January 15, 2017. -// -// In JavaScript, one can convert a Date object to this format using the -// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) -// method. In Python, a standard `datetime.datetime` object can be converted -// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) -// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one -// can use the Joda Time's [`ISODateTimeFormat.dateTime()`]( -// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D -// ) to obtain a formatter capable of generating timestamps in this format. -// -// -message Timestamp { - - // Represents seconds of UTC time since Unix epoch - // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to - // 9999-12-31T23:59:59Z inclusive. - int64 seconds = 1; - - // Non-negative fractions of a second at nanosecond resolution. Negative - // second values with fractions must still have non-negative nanos values - // that count forward in time. Must be from 0 to 999,999,999 - // inclusive. - int32 nanos = 2; -} diff --git a/lib/trace.json b/lib/trace.json deleted file mode 100644 index 56d2c761..00000000 --- a/lib/trace.json +++ /dev/null @@ -1,336 +0,0 @@ -{ - "nested": { - "opentelemetry": { - "nested": { - "proto": { - "nested": { - "trace": { - "nested": { - "v1": { - "options": { - "java_multiple_files": true, - "java_package": "io.opentelemetry.proto.trace.v1", - "java_outer_classname": "TraceProto", - "go_package": "github.com/open-telemetry/opentelemetry-proto/gen/go/trace/v1" - }, - "nested": { - "TracesData": { - "fields": { - "resourceSpans": { - "rule": "repeated", - "type": "ResourceSpans", - "id": 1 - } - } - }, - "ResourceSpans": { - "fields": { - "resource": { - "type": "opentelemetry_proto_resource_v1_Resource", - "id": 1 - }, - "instrumentationLibrarySpans": { - "rule": "repeated", - "type": "InstrumentationLibrarySpans", - "id": 2 - }, - "schemaUrl": { - "type": "string", - "id": 3 - } - } - }, - "InstrumentationLibrarySpans": { - "fields": { - "instrumentationLibrary": { - "type": "InstrumentationLibrary", - "id": 1 - }, - "spans": { - "rule": "repeated", - "type": "Span", - "id": 2 - }, - "schemaUrl": { - "type": "string", - "id": 3 - } - } - }, - "Span": { - "fields": { - "traceId": { - "type": "bytes", - "id": 1 - }, - "spanId": { - "type": "bytes", - "id": 2 - }, - "traceState": { - "type": "string", - "id": 3 - }, - "parentSpanId": { - "type": "bytes", - "id": 4 - }, - "name": { - "type": "string", - "id": 5 - }, - "kind": { - "type": "SpanKind", - "id": 6 - }, - "startTimeUnixNano": { - "type": "fixed64", - "id": 7 - }, - "endTimeUnixNano": { - "type": "fixed64", - "id": 8 - }, - "attributes": { - "rule": "repeated", - "type": "KeyValue", - "id": 9 - }, - "droppedAttributesCount": { - "type": "uint32", - "id": 10 - }, - "events": { - "rule": "repeated", - "type": "Event", - "id": 11 - }, - "droppedEventsCount": { - "type": "uint32", - "id": 12 - }, - "links": { - "rule": "repeated", - "type": "Link", - "id": 13 - }, - "droppedLinksCount": { - "type": "uint32", - "id": 14 - }, - "status": { - "type": "Status", - "id": 15 - } - }, - "nested": { - "SpanKind": { - "values": { - "SPAN_KIND_UNSPECIFIED": 0, - "SPAN_KIND_INTERNAL": 1, - "SPAN_KIND_SERVER": 2, - "SPAN_KIND_CLIENT": 3, - "SPAN_KIND_PRODUCER": 4, - "SPAN_KIND_CONSUMER": 5 - } - }, - "Event": { - "fields": { - "timeUnixNano": { - "type": "fixed64", - "id": 1 - }, - "name": { - "type": "string", - "id": 2 - }, - "attributes": { - "rule": "repeated", - "type": "KeyValue", - "id": 3 - }, - "droppedAttributesCount": { - "type": "uint32", - "id": 4 - } - } - }, - "Link": { - "fields": { - "traceId": { - "type": "bytes", - "id": 1 - }, - "spanId": { - "type": "bytes", - "id": 2 - }, - "traceState": { - "type": "string", - "id": 3 - }, - "attributes": { - "rule": "repeated", - "type": "KeyValue", - "id": 4 - }, - "droppedAttributesCount": { - "type": "uint32", - "id": 5 - } - } - } - } - }, - "Status": { - "fields": { - "message": { - "type": "string", - "id": 2 - }, - "code": { - "type": "StatusCode", - "id": 3 - } - }, - "reserved": [ - [ - 1, - 1 - ] - ], - "nested": { - "StatusCode": { - "values": { - "STATUS_CODE_UNSET": 0, - "STATUS_CODE_OK": 1, - "STATUS_CODE_ERROR": 2 - } - } - } - }, - "opentelemetry_proto_resource_v1_Resource": { - "fields": { - "attributes": { - "rule": "repeated", - "type": "KeyValue", - "id": 1 - }, - "droppedAttributesCount": { - "type": "uint32", - "id": 2 - } - } - }, - "AnyValue": { - "oneofs": { - "value": { - "oneof": [ - "stringValue", - "boolValue", - "intValue", - "doubleValue", - "arrayValue", - "kvlistValue", - "bytesValue" - ] - } - }, - "fields": { - "stringValue": { - "type": "string", - "id": 1 - }, - "boolValue": { - "type": "bool", - "id": 2 - }, - "intValue": { - "type": "int64", - "id": 3 - }, - "doubleValue": { - "type": "double", - "id": 4 - }, - "arrayValue": { - "type": "ArrayValue", - "id": 5 - }, - "kvlistValue": { - "type": "KeyValueList", - "id": 6 - }, - "bytesValue": { - "type": "bytes", - "id": 7 - } - } - }, - "ArrayValue": { - "fields": { - "values": { - "rule": "repeated", - "type": "AnyValue", - "id": 1 - } - } - }, - "KeyValueList": { - "fields": { - "values": { - "rule": "repeated", - "type": "KeyValue", - "id": 1 - } - } - }, - "KeyValue": { - "fields": { - "key": { - "type": "string", - "id": 1 - }, - "value": { - "type": "AnyValue", - "id": 2 - } - } - }, - "StringKeyValue": { - "options": { - "deprecated": true - }, - "fields": { - "key": { - "type": "string", - "id": 1 - }, - "value": { - "type": "string", - "id": 2 - } - } - }, - "InstrumentationLibrary": { - "fields": { - "name": { - "type": "string", - "id": 1 - }, - "version": { - "type": "string", - "id": 2 - } - } - } - } - } - } - } - } - } - } - } - } -} \ No newline at end of file diff --git a/lib/utils.js b/lib/utils.js deleted file mode 100644 index 7931b44d..00000000 --- a/lib/utils.js +++ /dev/null @@ -1,252 +0,0 @@ -const logger = require('./logger') -const stableStringify = require('json-stable-stringify') -/* Function Helpers */ - -/* Label Parser */ -const labelParser = function (labels) { - // Label Parser - const rx = /\"?\b(\w+)\"?(!?=~?)("[^"\n]*?")/g - let matches - const output = [] - matches = rx.exec(labels) - while (matches) { - if (matches.length > 3) output.push([matches[1], matches[2], matches[3].replace(/['"]+/g, '')]) - matches = rx.exec(labels) - } - let regex = false - try { - regex = /\}\s*(.*)/g.exec(labels)[1] || false - } catch (e) { - } - return { labels: output, regex: regex } -} -/* Fingerprinting */ -const shortHash = require('short-hash') -const { QrynBadRequest } = require('./handlers/errors') -let xxh = null -require('xxhash-wasm')().then((res) => { - xxh = res - logger.info('xxh ready') -}, (err) => { - logger.error(err) - process.exit(1) -}) -const fingerPrint = function (text, hex, alg) { - alg = alg || process.env.HASH || 'xxhash64' - switch (alg) { - case 'xxhash64': - if (!xxh) { - throw new Error('Hasher is not ready') - } - if (hex) return xxh.h64(text).toString() - else return BigInt('0x' + xxh.h64(text)) - } - if (hex) return shortHash(text) - else return parseInt(shortHash(text), 16) -} - -/** - * - * @param str {string} - * @returns Object - */ -const toJSON = (function () { - const labelsRe = /(^|[{,])\s*([a-zA-Z_][_a-zA-Z0-9]*)\s*=\s*("(\\.|[^"\\])+"|[a-zA-Z_][_a-zA-Z0-9]*)/g - /** - * - * @param str {string} - * @returns {Object<string, string>} - */ - const res = (str) => { - const res = {} - let i = 0 - for (const m of str.matchAll(labelsRe)) { - res[m[2]] = m[3].substring(0, 1) === '"' ? JSON.parse(m[3]) : m[3] - i++ - } - if (!i) { - throw new QrynBadRequest('empty labels set') - } - return res - } - return res -})() - -const parseOrDefault = (str, def) => { - try { - return str ? parseFloat(str) : def - } catch (e) { - return def - } -} - -/** - * @param str {String} - * @param def {Number} - * @return {Number} duration in sec or default - */ -const parseDurationSecOrDefault = (str, def) => { - const multiplier = { - ns: 1e9, - us: 1e6, - ms: 1e3, - s: 1, - m: 1 / 60, - h: 1 / 3600, - d: 1 / 3600 / 24, - w: 1 / 3600 / 24 / 7 - } - if (!str) { - return def - } - const match = str.toString().match(/^(?<num>[0-9.]+)(?<unit>ns|us|ms|s|m|h)?$/) - if (!match) { - return def - } - const unit = match.groups.unit || 's' - const num = parseFloat(match.groups.num) - return num / multiplier[unit] -} - -const parseMs = (time, def) => { - try { - return time ? Math.floor(parseInt(time) / 1000000) : def - } catch (e) { - return def - } -} - -/** - * - * @param time {string | BigInt} - * @return {BigInt | undefined} - */ -const parseStringifiedNanosOrRFC3339 = (time) => { - if (typeof time === 'bigint') { - return time - } - const iMatch = time.match(/^[0-9]+$/) - if (iMatch) { - // It is nanos - return BigInt(time) - } - const dMatch = time.match(/(?<y>[0-9]{4})-(?<m>[0-1][0-9])-(?<d>[0-3][0-9])T(?<h>[0-2][0-9]):(?<i>[0-6][0-9]):(?<s>[0-6][0-9])(?<ns>\.[0-9]+)?(?<offs>Z|(\+|-)(?<oh>[0-2][0-9]):(?<oi>[0-6][0-9]))/) - if (dMatch) { - const g = dMatch.groups - let iTime = Date.UTC(g.y, parseInt(g.m) - 1, g.d, g.h, g.i, g.s) - if (g.offs !== 'Z') { - iTime += (g.offs[0] === '+' ? 1 : -1) * (parseInt(g.oh) * 3600 * 1000 + parseInt(g.oi) * 60 * 1000) - } - const ns = g.ns ? BigInt((g.ns + '000000000').substr(1, 9)) : BigInt(0) - return BigInt(iTime) * BigInt(1e6) + ns - } -} - -/** - * - * @param labels {Object} - */ -const stringify = (labels) => { - labels = Object.fromEntries(Object.entries(labels).map( - e => typeof e === 'string' ? [e[0]?.trim || e[0], e[1]] : [e[0]?.trim() || e[0], `${e[1]}`] - )) - return stableStringify(labels) -} - -/** - * - * @param attrs {*[]} - * @returns {Object<string, string>} - */ -const flatOTLPAttrs = (attrs) => { - const flatVal = (key, val, prefix, res) => { - if (!val) { - return - } - for (const valueKey of ['stringValue', 'boolValue', 'intValue', 'doubleValue', 'bytesValue']) { - if (typeof val[valueKey] !== 'undefined') { - res[prefix + key] = `${val[valueKey]}` - return - } - } - if (val.arrayValue && val.arrayValue.values) { - val.arrayValue.values.forEach((v, i) => { - flatVal(`${i}`, v, `${prefix}${key}.`, res) - }) - return - } - if (val.kvlistValue) { - flatAttrs(val.kvlistValue.values, `${prefix}${key}.`, res) - } - } - const flatAttrs = (attrs, prefix, res) => { - for (const attr of attrs) { - if (!attr) { - continue - } - flatVal(attr.key, attr.value, prefix, res) - } - return res - } - return flatAttrs(attrs, '', {}) -} - -/** - * - * @param attrs {Object<string, string>} - * @returns {{local: string, remote: string | undefined}} - */ -const OTLPgetServiceNames = (attrs) => { - let local - let remote - for (const attr of ['peer.service', 'service.name', 'faas.name', 'k8s.deployment.name', 'process.executable.name']) { - if (attrs[attr]) { - local = attrs[attr] - break - } - } - for (const attr of ['service.name', 'faas.name', 'k8s.deployment.name', 'process.executable.name']) { - if (attrs[attr]) { - remote = attrs[attr] - break - } - } - local = local || 'OTLPResourceNoServiceName' - return { local, remote } -} - -const codeToString = (code) => { - switch (code) { - case 0: - return 'Unset' - case 1: - return 'Ok' - case 2: - return 'Error' - } -} - -let _samplesReadTableName = () => 'samples_read' -let _checkVersion = () => false - -module.exports.DATABASE_NAME = () => process.env.CLICKHOUSE_DB || 'cloki' -module.exports.fingerPrint = fingerPrint -module.exports.labelParser = labelParser -module.exports.toJSON = toJSON -module.exports.parseMs = parseMs -module.exports.parseOrDefault = parseOrDefault - -module.exports.onSamplesReadTableName = (fn) => { _samplesReadTableName = fn } -module.exports.onCheckVersion = (fn) => { _checkVersion = fn } - -module.exports.samplesReadTableName = (from) => _samplesReadTableName(from) -module.exports.checkVersion = (ver, from) => _checkVersion(ver, from) - -module.exports.schemaVer = 'v3' -module.exports.samplesTableName = 'samples_v3' -module.exports.parseStringifiedNanosOrRFC3339 = parseStringifiedNanosOrRFC3339 -module.exports.parseDurationSecOrDefault = parseDurationSecOrDefault -module.exports.stringify = stringify -module.exports.flatOTLPAttrs = flatOTLPAttrs -module.exports.OTLPgetServiceNames = OTLPgetServiceNames -module.exports.codeToString = codeToString diff --git a/main.go b/main.go new file mode 100644 index 00000000..929721e2 --- /dev/null +++ b/main.go @@ -0,0 +1,290 @@ +package main + +import ( + "flag" + "fmt" + "github.com/gorilla/mux" + clconfig "github.com/metrico/cloki-config" + "github.com/metrico/cloki-config/config" + "github.com/metrico/qryn/ctrl" + "github.com/metrico/qryn/reader" + "github.com/metrico/qryn/reader/utils/logger" + "github.com/metrico/qryn/reader/utils/middleware" + "github.com/metrico/qryn/shared/commonroutes" + "github.com/metrico/qryn/view" + "github.com/metrico/qryn/writer" + "net" + "net/http" + "os" + "strconv" + "strings" +) + +var appFlags CommandLineFlags + +// params for Flags +type CommandLineFlags struct { + InitializeDB *bool `json:"initialize_db"` + ShowHelpMessage *bool `json:"help"` + ShowVersion *bool `json:"version"` + ConfigPath *string `json:"config_path"` +} + +/* init flags */ +func initFlags() { + appFlags.InitializeDB = flag.Bool("initialize_db", false, "initialize the database and create all tables") + appFlags.ShowHelpMessage = flag.Bool("help", false, "show help") + appFlags.ShowVersion = flag.Bool("version", false, "show version") + appFlags.ConfigPath = flag.String("config", "", "the path to the config file") + flag.Parse() + +} + +func boolEnv(key string) (bool, error) { + val := os.Getenv("key") + for _, v := range []string{"true", "1", "yes", "y"} { + if v == val { + return true, nil + } + } + for _, v := range []string{"false", "0", "no", "n", ""} { + if v == val { + return false, nil + } + } + return false, fmt.Errorf("%s value must be one of [no, n, false, 0, yes, y, true, 1]", key) +} + +func initDB(cfg *clconfig.ClokiConfig) { + bVal, err := boolEnv("OMIT_CREATE_TABLES") + if err != nil { + panic(err) + } + if bVal { + return + } + err = ctrl.Init(cfg, "qryn") + if err != nil { + panic(err) + } + err = ctrl.Rotate(cfg, "qryn") + if err != nil { + panic(err) + } +} + +func portCHEnv(cfg *clconfig.ClokiConfig) error { + if len(cfg.Setting.DATABASE_DATA) > 0 { + return nil + } + cfg.Setting.DATABASE_DATA = []config.ClokiBaseDataBase{{ + ReadTimeout: 30, + WriteTimeout: 30, + }} + db := "cloki" + if os.Getenv("CLICKHOUSE_DB") != "" { + db = os.Getenv("CLICKHOUSE_DB") + } + cfg.Setting.DATABASE_DATA[0].Name = db + if os.Getenv("CLUSTER_NAME") != "" { + cfg.Setting.DATABASE_DATA[0].ClusterName = os.Getenv("CLUSTER_NAME") + } + server := "localhost" + if os.Getenv("CLICKHOUSE_SERVER") != "" { + server = os.Getenv("CLICKHOUSE_SERVER") + } + cfg.Setting.DATABASE_DATA[0].Host = server + //TODO: add to readme to change port to tcp (9000) instead of http + strPort := "9000" + if os.Getenv("CLICKHOUSE_PORT") != "" { + strPort = os.Getenv("CLICKHOUSE_PORT") + } + port, err := strconv.ParseUint(strPort, 10, 32) + if err != nil { + return fmt.Errorf("invalid port number: %w", err) + } + cfg.Setting.DATABASE_DATA[0].Port = uint32(port) + if os.Getenv("CLICKHOUSE_AUTH") != "" { + auth := strings.SplitN(os.Getenv("CLICKHOUSE_AUTH"), ":", 2) + cfg.Setting.DATABASE_DATA[0].User = auth[0] + if len(auth) > 1 { + cfg.Setting.DATABASE_DATA[0].Password = auth[1] + } + } + if os.Getenv("ADVANCED_SAMPLES_ORDERING") != "" { + cfg.Setting.DATABASE_DATA[0].SamplesOrdering = os.Getenv("ADVANCED_SAMPLES_ORDERING") + } + //TODO: add to readme + secure := false + if os.Getenv("CLICKHOUSE_PROTO") == "https" || os.Getenv("CLICKHOUSE_PROTO") == "tls" { + secure = true + } + cfg.Setting.DATABASE_DATA[0].Secure = secure + if os.Getenv("SELF_SIGNED_CERT") != "" { + insecureSkipVerify, err := boolEnv(os.Getenv("SELF_SIGNED_CERT")) + if err != nil { + return fmt.Errorf("invalid self_signed_cert value: %w", err) + } + cfg.Setting.DATABASE_DATA[0].InsecureSkipVerify = insecureSkipVerify + } + + cfg.Setting.DATABASE_DATA[0].TTLDays = 7 + if os.Getenv("SAMPLES_DAYS") != "" { + days, err := strconv.Atoi(os.Getenv("SAMPLES_DAYS")) + if err != nil { + return fmt.Errorf("invalid samples_days value: %w", err) + } + cfg.Setting.DATABASE_DATA[0].TTLDays = days + } + + if os.Getenv("STORAGE_POLICY") != "" { + cfg.Setting.DATABASE_DATA[0].StoragePolicy = os.Getenv("STORAGE_POLICY") + } + + return nil +} + +func portEnv(cfg *clconfig.ClokiConfig) error { + err := portCHEnv(cfg) + if err != nil { + return err + } + if os.Getenv("QRYN_LOGIN") != "" { + cfg.Setting.AUTH_SETTINGS.BASIC.Username = os.Getenv("QRYN_LOGIN") + } + if os.Getenv("CLOKI_LOGIN") != "" { + cfg.Setting.AUTH_SETTINGS.BASIC.Username = os.Getenv("CLOKI_LOGIN") + } + if os.Getenv("QRYN_PASSWORD") != "" { + cfg.Setting.AUTH_SETTINGS.BASIC.Password = os.Getenv("QRYN_PASSWORD") + } + if os.Getenv("CLOKI_PASSWORD") != "" { + cfg.Setting.AUTH_SETTINGS.BASIC.Password = os.Getenv("CLOKI_PASSWORD") + } + if os.Getenv("CORS_ALLOW_ORIGIN") != "" { + cfg.Setting.HTTP_SETTINGS.Cors.Enable = true + cfg.Setting.HTTP_SETTINGS.Cors.Origin = os.Getenv("CORS_ALLOW_ORIGIN") + } + if os.Getenv("PORT") != "" { + port, err := strconv.Atoi(os.Getenv("PORT")) + if err != nil { + return fmt.Errorf("invalid port number: %w", err) + } + cfg.Setting.HTTP_SETTINGS.Port = port + } + if os.Getenv("HOST") != "" { + cfg.Setting.HTTP_SETTINGS.Host = os.Getenv("HOST") + } + if cfg.Setting.HTTP_SETTINGS.Host == "" { + cfg.Setting.HTTP_SETTINGS.Host = "0.0.0.0" + } + if os.Getenv("ADVANCED_PROMETHEUS_MAX_SAMPLES") != "" { + maxSamples, err := strconv.Atoi(os.Getenv("ADVANCED_PROMETHEUS_MAX_SAMPLES")) + if err != nil { + return fmt.Errorf("invalid max samples value `%s`: %w", maxSamples, err) + } + cfg.Setting.SYSTEM_SETTINGS.MetricsMaxSamples = maxSamples + } + mode := "all" + if os.Getenv("MODE") != "" { + mode = os.Getenv("MODE") + } + readonly, err := boolEnv("READONLY") + if err != nil { + return err + } + if readonly && mode == "all" { + mode = "reader" + } + + cfg.Setting.SYSTEM_SETTINGS.Mode = mode + + if os.Getenv("BULK_MAX_SIZE_BYTES") != "" { + maxSize, err := strconv.ParseInt(os.Getenv("BULK_MAX_SIZE_BYTES"), 10, 63) + if err != nil { + return fmt.Errorf("invalid max size value `%s`: %w", maxSize, err) + } + cfg.Setting.SYSTEM_SETTINGS.DBBulk = maxSize + } + + strMaxAge := "100" + if os.Getenv("BULK_MAX_AGE_MS") != "" { + strMaxAge = os.Getenv("BULK_MAX_AGE_MS") + } + maxAge, err := strconv.Atoi(strMaxAge) + if err != nil { + return fmt.Errorf("invalid max age value `%s`: %w", maxAge, err) + } + cfg.Setting.SYSTEM_SETTINGS.DBTimer = float64(maxAge) / 1000 + return nil +} + +func main() { + initFlags() + var configPaths []string + if _, err := os.Stat(*appFlags.ConfigPath); err == nil { + configPaths = append(configPaths, *appFlags.ConfigPath) + } + cfg := clconfig.New(clconfig.CLOKI_READER, configPaths, "", "") + + cfg.ReadConfig() + + err := portEnv(cfg) + if err != nil { + panic(err) + } + if cfg.Setting.HTTP_SETTINGS.Port == 0 { + cfg.Setting.HTTP_SETTINGS.Port = 3100 + } + + initDB(cfg) + if os.Getenv("MODE") == "init_only" { + return + } + + app := mux.NewRouter() + if cfg.Setting.AUTH_SETTINGS.BASIC.Username != "" && + cfg.Setting.AUTH_SETTINGS.BASIC.Password != "" { + app.Use(middleware.BasicAuthMiddleware(cfg.Setting.AUTH_SETTINGS.BASIC.Username, + cfg.Setting.AUTH_SETTINGS.BASIC.Password)) + } + app.Use(middleware.AcceptEncodingMiddleware) + if cfg.Setting.HTTP_SETTINGS.Cors.Enable { + app.Use(middleware.CorsMiddleware(cfg.Setting.HTTP_SETTINGS.Cors.Origin)) + } + app.Use(middleware.LoggingMiddleware("[{{.status}}] {{.method}} {{.url}} - LAT:{{.latency}}")) + commonroutes.RegisterCommonRoutes(app) + cfg.Setting.LOG_SETTINGS.Level = "debug" + cfg.Setting.LOG_SETTINGS.Stdout = true + if cfg.Setting.SYSTEM_SETTINGS.Mode == "all" || + cfg.Setting.SYSTEM_SETTINGS.Mode == "writer" || + cfg.Setting.SYSTEM_SETTINGS.Mode == "" { + writer.Init(cfg, app) + } + if cfg.Setting.SYSTEM_SETTINGS.Mode == "all" || + cfg.Setting.SYSTEM_SETTINGS.Mode == "reader" || + cfg.Setting.SYSTEM_SETTINGS.Mode == "" { + reader.Init(cfg, app) + view.Init(cfg, app) + } + + initPyro() + + httpURL := fmt.Sprintf("%s:%d", cfg.Setting.HTTP_SETTINGS.Host, cfg.Setting.HTTP_SETTINGS.Port) + httpStart(app, httpURL) +} + +func httpStart(server *mux.Router, httpURL string) { + logger.Info("Starting service") + http.Handle("/", server) + listener, err := net.Listen("tcp", httpURL) + if err != nil { + logger.Error("Error creating listener:", err) + panic(err) + } + logger.Info("Server is listening on", httpURL) + if err := http.Serve(listener, server); err != nil { + logger.Error("Error serving:", err) + panic(err) + } +} diff --git a/model/TRAINING.md b/model/TRAINING.md deleted file mode 100644 index 8b137891..00000000 --- a/model/TRAINING.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index f0792f4d..00000000 --- a/package-lock.json +++ /dev/null @@ -1,10997 +0,0 @@ -{ - "name": "qryn", - "version": "3.1.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "qryn", - "version": "3.1.1", - "hasInstallScript": true, - "license": "AGPL-3.0", - "dependencies": { - "@apla/clickhouse": "^1.6.4", - "@cloki/clickhouse-sql": "1.2.10", - "@fastify/basic-auth": "^5.1.0", - "@fastify/compress": "^6.5.0", - "@fastify/cors": "^8.4.1", - "@fastify/formbody": "^7.4.0", - "@fastify/static": "^6.12.0", - "@fastify/url-data": "^5.4.0", - "@fastify/websocket": "^8.2.0", - "@grpc/grpc-js": "^1.10.6", - "@grpc/proto-loader": "^0.7.12", - "@qxip/influx-line-protocol-parser": "^0.2.1", - "@qxip/plugnplay": "^3.3.1", - "@stricjs/router": "^5.0.6", - "@stricjs/utils": "^1.6.1", - "axios": "^1.6.8", - "basic-auth": "^2.0.1", - "bnf": "^1.0.1", - "csv-writer": "^1.6.0", - "date-fns": "^2.27.0", - "fast-querystring": "^1.1.0", - "fastify": "^4.24.3", - "fastify-metrics": "^10.3.3", - "fastify-plugin": "^4.5.1", - "glob": "^7.1.2", - "google-protobuf": "^3.21.2", - "handlebars": "^4.7.7", - "handlebars-utils": "^1.0.6", - "http-errors": "^2.0.0", - "is-number": "^7.0.0", - "json-stable-stringify": "^1.0.1", - "jsonic": "^1.0.1", - "logfmt": "^1.3.2", - "node-gzip": "^1.1.2", - "pako": "^2.1.0", - "patch-package": "^6.4.7", - "pino": "^7.6.5", - "plugnplay": "npm:@qxip/plugnplay@^3.3.1", - "protobufjs": "7.2.6", - "protocol-buffers": "^4.2.0", - "protocol-buffers-encodings": "^1.1.1", - "record-cache": "^1.1.1", - "scramjet": "^4.36.1", - "short-hash": "^1.0.0", - "snappyjs": "^0.6.1", - "stream-chain": "^2.2.4", - "stream-json": "^1.7.3", - "ws": "^8.17.1", - "xxhash-wasm": "^0.4.2", - "yaml": "^1.10.2" - }, - "bin": { - "qryn": "qryn_node.js" - }, - "devDependencies": { - "@elastic/elasticsearch": "=8.6.0", - "@influxdata/influxdb-client": "^1.33.2", - "@opentelemetry/api": "^1.0.2", - "@opentelemetry/exporter-trace-otlp-proto": "^0.50.0", - "@opentelemetry/instrumentation": "^0.25.0", - "@opentelemetry/instrumentation-connect": "^0.30.0", - "@opentelemetry/instrumentation-http": "^0.25.0", - "@opentelemetry/resources": "^0.25.0", - "@opentelemetry/sdk-trace-base": "^0.25.0", - "@opentelemetry/sdk-trace-node": "^0.25.0", - "@opentelemetry/semantic-conventions": "^0.25.0", - "casual": "^1.6.2", - "eslint": "^7.32.0", - "eslint-config-standard": "^16.0.3", - "eslint-plugin-import": "^2.25.3", - "eslint-plugin-jest": "^25.3.0", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^5.2.0", - "jest": "^29.7.0", - "node-abort-controller": "^3.1.1", - "node-fetch": "^2.6.7", - "pino-pretty": "^7.5.1", - "prometheus-remote-write": "^0.3.0", - "protobufjs": ">=7.2.4", - "standard": "^16.0.4" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@apla/clickhouse": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/@apla/clickhouse/-/clickhouse-1.6.4.tgz", - "integrity": "sha512-zOLL/lOaXrk+l8XS8eDGZfqOy1yKwYQirQtHtyrLxAHlJMNRKBosjK2gVUyCZ94fAIpE8lLUZRxFDid2Vmqxnw==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", - "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", - "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-module-transforms": "^7.25.2", - "@babel/helpers": "^7.25.0", - "@babel/parser": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.2", - "@babel/types": "^7.25.2", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", - "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.25.6", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", - "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.25.2", - "@babel/helper-validator-option": "^7.24.8", - "browserslist": "^4.23.1", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", - "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", - "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", - "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", - "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", - "dev": true, - "dependencies": { - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", - "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", - "dev": true, - "dependencies": { - "@babel/types": "^7.25.6" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.6.tgz", - "integrity": "sha512-sXaDXaJN9SNLymBdlWFA+bjzBhFD617ZaFiY13dGt7TVslVvVgA6fkZOP7Ki3IGElC45lwHdOTrCtKZGVAWeLQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", - "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.4.tgz", - "integrity": "sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", - "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template/node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", - "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.6", - "@babel/parser": "^7.25.6", - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.6", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", - "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@cloki/clickhouse-sql": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@cloki/clickhouse-sql/-/clickhouse-sql-1.2.10.tgz", - "integrity": "sha512-y1WeeAxCYB2T/I/3Pn2mVQXXpMitRwN4R1SUNz69wrRJgosMKLx5p/5cPKAVxITbRW8+h8uRaDcWadr6vIuDeQ==" - }, - "node_modules/@elastic/elasticsearch": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/@elastic/elasticsearch/-/elasticsearch-8.6.0.tgz", - "integrity": "sha512-mN5EbbgSp1rfRmQ/5Hv7jqAK8xhGJxCg7G84xje8hSefE59P+HPPCv/+DgesCUSJdZpwXIo0DwOWHfHvktxxLw==", - "dev": true, - "dependencies": { - "@elastic/transport": "^8.3.1", - "tslib": "^2.4.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@elastic/transport": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/@elastic/transport/-/transport-8.3.1.tgz", - "integrity": "sha512-jv/Yp2VLvv5tSMEOF8iGrtL2YsYHbpf4s+nDsItxUTLFTzuJGpnsB/xBlfsoT2kAYEnWHiSJuqrbRcpXEI/SEQ==", - "dev": true, - "dependencies": { - "debug": "^4.3.4", - "hpagent": "^1.0.0", - "ms": "^2.1.3", - "secure-json-parse": "^2.4.0", - "tslib": "^2.4.0", - "undici": "^5.5.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@elastic/transport/node_modules/undici": { - "version": "5.28.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", - "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", - "dev": true, - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, - "engines": { - "node": ">=14.0" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/@fastify/accept-negotiator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz", - "integrity": "sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==", - "engines": { - "node": ">=14" - } - }, - "node_modules/@fastify/ajv-compiler": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.6.0.tgz", - "integrity": "sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==", - "dependencies": { - "ajv": "^8.11.0", - "ajv-formats": "^2.1.1", - "fast-uri": "^2.0.0" - } - }, - "node_modules/@fastify/ajv-compiler/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@fastify/ajv-compiler/node_modules/ajv/node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" - }, - "node_modules/@fastify/ajv-compiler/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/@fastify/basic-auth": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@fastify/basic-auth/-/basic-auth-5.1.1.tgz", - "integrity": "sha512-L4b7EK5LKZnV6fdH1+rQbjhkKGXjCfiKJ0JkdGHZQPBMHMiXDZF8xbZsCakWGf9c7jDXJicP3FPcIXUPBkuSeQ==", - "dependencies": { - "@fastify/error": "^3.0.0", - "fastify-plugin": "^4.0.0" - } - }, - "node_modules/@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@fastify/compress": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@fastify/compress/-/compress-6.5.0.tgz", - "integrity": "sha512-AqUOK714jY7qkzbQbS4zyI4yNFgnRoOJ3eH/oV1T9f5fFdPDRdrFxm5de1ya5n+as4bvitjwU9EY7zvtT9pI2A==", - "dependencies": { - "@fastify/accept-negotiator": "^1.1.0", - "fastify-plugin": "^4.5.0", - "into-stream": "^6.0.0", - "mime-db": "^1.52.0", - "minipass": "^7.0.2", - "peek-stream": "^1.1.3", - "pump": "^3.0.0", - "pumpify": "^2.0.1" - } - }, - "node_modules/@fastify/cors": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-8.5.0.tgz", - "integrity": "sha512-/oZ1QSb02XjP0IK1U0IXktEsw/dUBTxJOW7IpIeO8c/tNalw/KjoNSJv1Sf6eqoBPO+TDGkifq6ynFK3v68HFQ==", - "dependencies": { - "fastify-plugin": "^4.0.0", - "mnemonist": "0.39.6" - } - }, - "node_modules/@fastify/error": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", - "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==" - }, - "node_modules/@fastify/fast-json-stringify-compiler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz", - "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==", - "dependencies": { - "fast-json-stringify": "^5.7.0" - } - }, - "node_modules/@fastify/formbody": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@fastify/formbody/-/formbody-7.4.0.tgz", - "integrity": "sha512-H3C6h1GN56/SMrZS8N2vCT2cZr7mIHzBHzOBa5OPpjfB/D6FzP9mMpE02ZzrFX0ANeh0BAJdoXKOF2e7IbV+Og==", - "dependencies": { - "fast-querystring": "^1.0.0", - "fastify-plugin": "^4.0.0" - } - }, - "node_modules/@fastify/merge-json-schemas": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz", - "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - } - }, - "node_modules/@fastify/send": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz", - "integrity": "sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==", - "dependencies": { - "@lukeed/ms": "^2.0.1", - "escape-html": "~1.0.3", - "fast-decode-uri-component": "^1.0.1", - "http-errors": "2.0.0", - "mime": "^3.0.0" - } - }, - "node_modules/@fastify/static": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@fastify/static/-/static-6.12.0.tgz", - "integrity": "sha512-KK1B84E6QD/FcQWxDI2aiUCwHxMJBI1KeCUzm1BwYpPY1b742+jeKruGHP2uOluuM6OkBPI8CIANrXcCRtC2oQ==", - "dependencies": { - "@fastify/accept-negotiator": "^1.0.0", - "@fastify/send": "^2.0.0", - "content-disposition": "^0.5.3", - "fastify-plugin": "^4.0.0", - "glob": "^8.0.1", - "p-limit": "^3.1.0" - } - }, - "node_modules/@fastify/static/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@fastify/static/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@fastify/static/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@fastify/url-data": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@fastify/url-data/-/url-data-5.4.0.tgz", - "integrity": "sha512-Ht8lAZnIGsBKdFu6yYwN98OH89fFziCMeCq3yyZbIUKl+lbK5njhmPrcxAwfwLW1QoVk6OO0BkUPu1E/3Js94A==", - "dependencies": { - "fast-uri": "^2.2.0", - "fastify-plugin": "^4.0.0" - } - }, - "node_modules/@fastify/websocket": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/@fastify/websocket/-/websocket-8.3.1.tgz", - "integrity": "sha512-hsQYHHJme/kvP3ZS4v/WMUznPBVeeQHHwAoMy1LiN6m/HuPfbdXq1MBJ4Nt8qX1YI+eVbog4MnOsU7MTozkwYA==", - "dependencies": { - "fastify-plugin": "^4.0.0", - "ws": "^8.0.0" - } - }, - "node_modules/@grpc/grpc-js": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.11.1.tgz", - "integrity": "sha512-gyt/WayZrVPH2w/UTLansS7F9Nwld472JxxaETamrM8HNlsa+jSLNyKAZmhxI2Me4c3mQHFiS1wWHDY1g1Kthw==", - "dependencies": { - "@grpc/proto-loader": "^0.7.13", - "@js-sdsl/ordered-map": "^4.4.2" - }, - "engines": { - "node": ">=12.10.0" - } - }, - "node_modules/@grpc/proto-loader": { - "version": "0.7.13", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", - "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", - "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.2.5", - "yargs": "^17.7.2" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true - }, - "node_modules/@influxdata/influxdb-client": { - "version": "1.35.0", - "resolved": "https://registry.npmjs.org/@influxdata/influxdb-client/-/influxdb-client-1.35.0.tgz", - "integrity": "sha512-woWMi8PDpPQpvTsRaUw4Ig+nOGS/CWwAwS66Fa1Vr/EkW+NEwxI8YfPBsdBMn33jK2Y86/qMiiuX/ROHIkJLTw==", - "dev": true - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@js-sdsl/ordered-map": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", - "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/@lukeed/ms": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", - "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@opentelemetry/api": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.8.0.tgz", - "integrity": "sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/api-logs": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.50.0.tgz", - "integrity": "sha512-JdZuKrhOYggqOpUljAq4WWNi5nB10PmgoF0y2CvedLGXd0kSawb/UBnWT8gg1ND3bHCNHStAIVT0ELlxJJRqrA==", - "dev": true, - "dependencies": { - "@opentelemetry/api": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/api-metrics": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-metrics/-/api-metrics-0.25.0.tgz", - "integrity": "sha512-9T0c9NQAEGRujUC7HzPa2/qZ5px/UvB2sfSU5CAKFRrAlDl2gn25B0oUbDqSRHW/IG1X2rnQ3z2bBQkJyJvE4g==", - "deprecated": "Please use @opentelemetry/api >= 1.3.0", - "dev": true, - "engines": { - "node": ">=8.0.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.2" - } - }, - "node_modules/@opentelemetry/context-async-hooks": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-0.25.0.tgz", - "integrity": "sha512-XNjlBalbN82qCfkgPpof6g3oU/LZoyoGGrluA+cy4AKWjJ9FdEZqKwX2p2WHxEuWm8TrHh5HxqEXH5OH2o/5tw==", - "dev": true, - "engines": { - "node": ">=8.1.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.2" - } - }, - "node_modules/@opentelemetry/core": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.23.0.tgz", - "integrity": "sha512-hdQ/a9TMzMQF/BO8Cz1juA43/L5YGtCSiKoOHmrTEf7VMDAZgy8ucpWx3eQTnQ3gBloRcWtzvcrMZABC3PTSKQ==", - "dev": true, - "dependencies": { - "@opentelemetry/semantic-conventions": "1.23.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.9.0" - } - }, - "node_modules/@opentelemetry/core/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.23.0.tgz", - "integrity": "sha512-MiqFvfOzfR31t8cc74CTP1OZfz7MbqpAnLCra8NqQoaHJX6ncIRTdYOQYBDQ2uFISDq0WY8Y9dDTWvsgzzBYRg==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-proto": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.50.0.tgz", - "integrity": "sha512-vavD9Ow6yOLiD+ocuS/oeciCsXNdsN41aYUrEljNaLXogvnkfMhJ+JLAhOnRSpzlVtRp7Ciw2BYGdYSebR0OsA==", - "dev": true, - "dependencies": { - "@opentelemetry/core": "1.23.0", - "@opentelemetry/otlp-exporter-base": "0.50.0", - "@opentelemetry/otlp-proto-exporter-base": "0.50.0", - "@opentelemetry/otlp-transformer": "0.50.0", - "@opentelemetry/resources": "1.23.0", - "@opentelemetry/sdk-trace-base": "1.23.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.23.0.tgz", - "integrity": "sha512-iPRLfVfcEQynYGo7e4Di+ti+YQTAY0h5mQEUJcHlU9JOqpb4x965O6PZ+wMcwYVY63G96KtdS86YCM1BF1vQZg==", - "dev": true, - "dependencies": { - "@opentelemetry/core": "1.23.0", - "@opentelemetry/semantic-conventions": "1.23.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.9.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.23.0.tgz", - "integrity": "sha512-PzBmZM8hBomUqvCddF/5Olyyviayka44O5nDWq673np3ctnvwMOvNrsUORZjKja1zJbwEuD9niAGbnVrz3jwRQ==", - "dev": true, - "dependencies": { - "@opentelemetry/core": "1.23.0", - "@opentelemetry/resources": "1.23.0", - "@opentelemetry/semantic-conventions": "1.23.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.9.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.23.0.tgz", - "integrity": "sha512-MiqFvfOzfR31t8cc74CTP1OZfz7MbqpAnLCra8NqQoaHJX6ncIRTdYOQYBDQ2uFISDq0WY8Y9dDTWvsgzzBYRg==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/instrumentation": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.25.0.tgz", - "integrity": "sha512-G9V2ISxrgUgdckZHZXu+kzeEgC76vRyZXlRdwXOT6VsJKHE+UENnC/502wNtPCFERgWSJ56W/51egkgkcKp6bA==", - "dev": true, - "dependencies": { - "@opentelemetry/api-metrics": "0.25.0", - "require-in-the-middle": "^5.0.3", - "semver": "^7.3.2", - "shimmer": "^1.2.1" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.2" - } - }, - "node_modules/@opentelemetry/instrumentation-connect": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.30.1.tgz", - "integrity": "sha512-holfuVSpNWuU/yaLugYLArWBwoWAcAGoHpfgNEM8qEGIlYDq1dWtsJvUVJ90YZmvN+vAJPfWOQnZ9jBcNudb4w==", - "dev": true, - "dependencies": { - "@opentelemetry/core": "^1.0.0", - "@opentelemetry/instrumentation": "^0.32.0", - "@opentelemetry/semantic-conventions": "^1.0.0", - "@types/connect": "3.4.35" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-connect/node_modules/@opentelemetry/api-metrics": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-metrics/-/api-metrics-0.32.0.tgz", - "integrity": "sha512-g1WLhpG8B6iuDyZJFRGsR+JKyZ94m5LEmY2f+duEJ9Xb4XRlLHrZvh6G34OH6GJ8iDHxfHb/sWjJ1ZpkI9yGMQ==", - "deprecated": "Please use @opentelemetry/api >= 1.3.0", - "dev": true, - "dependencies": { - "@opentelemetry/api": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/instrumentation-connect/node_modules/@opentelemetry/instrumentation": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.32.0.tgz", - "integrity": "sha512-y6ADjHpkUz/v1nkyyYjsQa/zorhX+0qVGpFvXMcbjU4sHnBnC02c6wcc93sIgZfiQClIWo45TGku1KQxJ5UUbQ==", - "dev": true, - "dependencies": { - "@opentelemetry/api-metrics": "0.32.0", - "require-in-the-middle": "^5.0.3", - "semver": "^7.3.2", - "shimmer": "^1.2.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-connect/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", - "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/instrumentation-http": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.25.0.tgz", - "integrity": "sha512-unQsS48RmAD/4za8gm8fvNCjrxq/iVxDJ+qO9PLbijET95ZTnS0FTq8rDS+/p9Q2X1REB1h9PmuCZc+3JehkcA==", - "dev": true, - "dependencies": { - "@opentelemetry/core": "0.25.0", - "@opentelemetry/instrumentation": "0.25.0", - "@opentelemetry/semantic-conventions": "0.25.0", - "semver": "^7.3.5" - }, - "engines": { - "node": ">=8.0.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.2" - } - }, - "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/core": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-0.25.0.tgz", - "integrity": "sha512-8OTWF4vfCENU112XB5ElLqf0eq/FhsY0SBvvY65vB3+fbZ2Oi+CPsRASrUZWGtC9MJ5rK2lBlY+/jI4a/NPPBg==", - "dev": true, - "dependencies": { - "@opentelemetry/semantic-conventions": "0.25.0", - "semver": "^7.3.5" - }, - "engines": { - "node": ">=8.5.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.2" - } - }, - "node_modules/@opentelemetry/otlp-exporter-base": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.50.0.tgz", - "integrity": "sha512-JUmjmrCmE1/fc4LjCQMqLfudgSl5OpUkzx7iA94b4jgeODM7zWxUoVXL7/CT7fWf47Cn+pmKjMvTCSESqZZ3mA==", - "dev": true, - "dependencies": { - "@opentelemetry/core": "1.23.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/otlp-proto-exporter-base": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-proto-exporter-base/-/otlp-proto-exporter-base-0.50.0.tgz", - "integrity": "sha512-hlbn3eZbhxoK79Sq1ddj1f7qcx+PzsPQC/SFpJvaWgTaqacCbqJmpzWDKfRRCAC7iGX2Hj/sgpf8vysazqyMOw==", - "dev": true, - "dependencies": { - "@opentelemetry/core": "1.23.0", - "@opentelemetry/otlp-exporter-base": "0.50.0", - "protobufjs": "^7.2.3" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/otlp-transformer": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.50.0.tgz", - "integrity": "sha512-s0sl1Yfqd5q1Kjrf6DqXPWzErL+XHhrXOfejh4Vc/SMTNqC902xDsC8JQxbjuramWt/+hibfguIvi7Ns8VLolA==", - "dev": true, - "dependencies": { - "@opentelemetry/api-logs": "0.50.0", - "@opentelemetry/core": "1.23.0", - "@opentelemetry/resources": "1.23.0", - "@opentelemetry/sdk-logs": "0.50.0", - "@opentelemetry/sdk-metrics": "1.23.0", - "@opentelemetry/sdk-trace-base": "1.23.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.9.0" - } - }, - "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.23.0.tgz", - "integrity": "sha512-iPRLfVfcEQynYGo7e4Di+ti+YQTAY0h5mQEUJcHlU9JOqpb4x965O6PZ+wMcwYVY63G96KtdS86YCM1BF1vQZg==", - "dev": true, - "dependencies": { - "@opentelemetry/core": "1.23.0", - "@opentelemetry/semantic-conventions": "1.23.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.9.0" - } - }, - "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.23.0.tgz", - "integrity": "sha512-PzBmZM8hBomUqvCddF/5Olyyviayka44O5nDWq673np3ctnvwMOvNrsUORZjKja1zJbwEuD9niAGbnVrz3jwRQ==", - "dev": true, - "dependencies": { - "@opentelemetry/core": "1.23.0", - "@opentelemetry/resources": "1.23.0", - "@opentelemetry/semantic-conventions": "1.23.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.9.0" - } - }, - "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.23.0.tgz", - "integrity": "sha512-MiqFvfOzfR31t8cc74CTP1OZfz7MbqpAnLCra8NqQoaHJX6ncIRTdYOQYBDQ2uFISDq0WY8Y9dDTWvsgzzBYRg==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/propagator-b3": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-0.25.0.tgz", - "integrity": "sha512-FMdy4YOZO56w2+pxsXWARWa+2F8N7fHW+ZfSFB937Q/oyOZ/2dhj3Ep12gqIH/qV6+kAblgiqOAMyOcnwBCUog==", - "dev": true, - "dependencies": { - "@opentelemetry/core": "0.25.0" - }, - "engines": { - "node": ">=8.0.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.2" - } - }, - "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-0.25.0.tgz", - "integrity": "sha512-8OTWF4vfCENU112XB5ElLqf0eq/FhsY0SBvvY65vB3+fbZ2Oi+CPsRASrUZWGtC9MJ5rK2lBlY+/jI4a/NPPBg==", - "dev": true, - "dependencies": { - "@opentelemetry/semantic-conventions": "0.25.0", - "semver": "^7.3.5" - }, - "engines": { - "node": ">=8.5.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.2" - } - }, - "node_modules/@opentelemetry/propagator-jaeger": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-0.25.0.tgz", - "integrity": "sha512-GipAgda8xTQa5YrnCQBzWVcUQO7vMsz2AROFse3QXnmgdKz07bIBQFqQLXvr4SHr38LiOVpZWe7Nvfqtuz/0HA==", - "dev": true, - "dependencies": { - "@opentelemetry/core": "0.25.0" - }, - "engines": { - "node": ">=8.5.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.2" - } - }, - "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-0.25.0.tgz", - "integrity": "sha512-8OTWF4vfCENU112XB5ElLqf0eq/FhsY0SBvvY65vB3+fbZ2Oi+CPsRASrUZWGtC9MJ5rK2lBlY+/jI4a/NPPBg==", - "dev": true, - "dependencies": { - "@opentelemetry/semantic-conventions": "0.25.0", - "semver": "^7.3.5" - }, - "engines": { - "node": ">=8.5.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.2" - } - }, - "node_modules/@opentelemetry/resources": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-0.25.0.tgz", - "integrity": "sha512-O46u53vDBlxCML8O9dIjsRcCC2VT5ri1upwhp02ITobgJ16aVD/iScCo1lPl/x2E7yq9uwzMINENiiYZRFb6XA==", - "dev": true, - "dependencies": { - "@opentelemetry/core": "0.25.0", - "@opentelemetry/semantic-conventions": "0.25.0" - }, - "engines": { - "node": ">=8.0.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.2" - } - }, - "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/core": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-0.25.0.tgz", - "integrity": "sha512-8OTWF4vfCENU112XB5ElLqf0eq/FhsY0SBvvY65vB3+fbZ2Oi+CPsRASrUZWGtC9MJ5rK2lBlY+/jI4a/NPPBg==", - "dev": true, - "dependencies": { - "@opentelemetry/semantic-conventions": "0.25.0", - "semver": "^7.3.5" - }, - "engines": { - "node": ">=8.5.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.2" - } - }, - "node_modules/@opentelemetry/sdk-logs": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.50.0.tgz", - "integrity": "sha512-PeUEupBB29p9nlPNqXoa1PUWNLsZnxG0DCDj3sHqzae+8y76B/A5hvZjg03ulWdnvBLYpnJslqzylG9E0IL87g==", - "dev": true, - "dependencies": { - "@opentelemetry/core": "1.23.0", - "@opentelemetry/resources": "1.23.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.4.0 <1.9.0", - "@opentelemetry/api-logs": ">=0.39.1" - } - }, - "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.23.0.tgz", - "integrity": "sha512-iPRLfVfcEQynYGo7e4Di+ti+YQTAY0h5mQEUJcHlU9JOqpb4x965O6PZ+wMcwYVY63G96KtdS86YCM1BF1vQZg==", - "dev": true, - "dependencies": { - "@opentelemetry/core": "1.23.0", - "@opentelemetry/semantic-conventions": "1.23.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.9.0" - } - }, - "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.23.0.tgz", - "integrity": "sha512-MiqFvfOzfR31t8cc74CTP1OZfz7MbqpAnLCra8NqQoaHJX6ncIRTdYOQYBDQ2uFISDq0WY8Y9dDTWvsgzzBYRg==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/sdk-metrics": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.23.0.tgz", - "integrity": "sha512-4OkvW6+wST4h6LFG23rXSTf6nmTf201h9dzq7bE0z5R9ESEVLERZz6WXwE7PSgg1gdjlaznm1jLJf8GttypFDg==", - "dev": true, - "dependencies": { - "@opentelemetry/core": "1.23.0", - "@opentelemetry/resources": "1.23.0", - "lodash.merge": "^4.6.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.9.0" - } - }, - "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/resources": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.23.0.tgz", - "integrity": "sha512-iPRLfVfcEQynYGo7e4Di+ti+YQTAY0h5mQEUJcHlU9JOqpb4x965O6PZ+wMcwYVY63G96KtdS86YCM1BF1vQZg==", - "dev": true, - "dependencies": { - "@opentelemetry/core": "1.23.0", - "@opentelemetry/semantic-conventions": "1.23.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.9.0" - } - }, - "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.23.0.tgz", - "integrity": "sha512-MiqFvfOzfR31t8cc74CTP1OZfz7MbqpAnLCra8NqQoaHJX6ncIRTdYOQYBDQ2uFISDq0WY8Y9dDTWvsgzzBYRg==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/sdk-trace-base": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.0.tgz", - "integrity": "sha512-TInkLSF/ThM3GNVM+9tgnCVjyNLnRxvAkG585Fhu0HNwaEtCTUwI0r7AvMRIREOreeRWttBG6kvT0LOKdo8yjw==", - "dev": true, - "dependencies": { - "@opentelemetry/core": "0.25.0", - "@opentelemetry/resources": "0.25.0", - "@opentelemetry/semantic-conventions": "0.25.0", - "lodash.merge": "^4.6.2" - }, - "engines": { - "node": ">=8.0.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.2" - } - }, - "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/core": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-0.25.0.tgz", - "integrity": "sha512-8OTWF4vfCENU112XB5ElLqf0eq/FhsY0SBvvY65vB3+fbZ2Oi+CPsRASrUZWGtC9MJ5rK2lBlY+/jI4a/NPPBg==", - "dev": true, - "dependencies": { - "@opentelemetry/semantic-conventions": "0.25.0", - "semver": "^7.3.5" - }, - "engines": { - "node": ">=8.5.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.2" - } - }, - "node_modules/@opentelemetry/sdk-trace-node": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-0.25.0.tgz", - "integrity": "sha512-j3bI3uhopgowdrJWIWkee/W5j0zeNk5Wydi7YGLayci+g/Ue3QYJaKlqaK2V7Rda1N8GY5d+k1CGQ3vE0DCOoQ==", - "dev": true, - "dependencies": { - "@opentelemetry/context-async-hooks": "0.25.0", - "@opentelemetry/core": "0.25.0", - "@opentelemetry/propagator-b3": "0.25.0", - "@opentelemetry/propagator-jaeger": "0.25.0", - "@opentelemetry/sdk-trace-base": "0.25.0", - "semver": "^7.3.5" - }, - "engines": { - "node": ">=8.0.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.2" - } - }, - "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-0.25.0.tgz", - "integrity": "sha512-8OTWF4vfCENU112XB5ElLqf0eq/FhsY0SBvvY65vB3+fbZ2Oi+CPsRASrUZWGtC9MJ5rK2lBlY+/jI4a/NPPBg==", - "dev": true, - "dependencies": { - "@opentelemetry/semantic-conventions": "0.25.0", - "semver": "^7.3.5" - }, - "engines": { - "node": ">=8.5.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.2" - } - }, - "node_modules/@opentelemetry/semantic-conventions": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-0.25.0.tgz", - "integrity": "sha512-V3N+MDBiv0TUlorbgiSqk6CvcP876CYUk/41Tg6s8OIyvniTwprE6vPvFQayuABiVkGlHOxv1Mlvp0w4qNdnVg==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, - "node_modules/@qxip/copy": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@qxip/copy/-/copy-0.3.4.tgz", - "integrity": "sha512-3uCplsC5PUJ3HBPJfhZGJOrDvOs/Ift/zK6zhtZPmOnv8FqAXCg/WEgGF0Sb/XtdJ3l8qCC87eCIWf2+GWakvw==", - "dependencies": { - "@qxip/to-file": "^0.2.1", - "async-each": "^1.0.0", - "bluebird": "^3.4.1", - "extend-shallow": "^2.0.1", - "file-contents": "^0.3.1", - "glob-parent": "^6.0.2", - "graceful-fs": "^4.1.4", - "has-glob": "^0.1.1", - "is-absolute": "^0.2.5", - "lazy-cache": "^2.0.1", - "log-ok": "^0.1.1", - "matched": "^0.4.1", - "mkdirp": "^0.5.1", - "resolve-dir": "^0.1.0" - }, - "bin": { - "copy": "bin/cli.js", - "copy-cli": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@qxip/influx-line-protocol-parser": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@qxip/influx-line-protocol-parser/-/influx-line-protocol-parser-0.2.1.tgz", - "integrity": "sha512-JMrafn5UhsE+1+G8KlKKbWREXxScMSOFPzc/CNgUr8RaL5KsCCHWpcCH3LLVIncruJ2PozLzpD2zBCn5yL81ag==", - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/@qxip/plugnplay": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@qxip/plugnplay/-/plugnplay-3.3.1.tgz", - "integrity": "sha512-Y/MoisMGHwgtcV9qjyOERhPTqJsdBaPXEdLK0i6U5cntygXWeX74Em0ttWcIawy1bO9w2uhxc2WhfB/p9Ex1bQ==", - "dependencies": { - "@qxip/copy": "^0.3.4", - "easy-factory": "^1.3.0", - "flatted": "^3.0.5", - "flow-copy-source": "^2.0.9", - "flow-remove-types": "^1.2.3", - "glob": "^7.1.2", - "js-yaml": "^3.14.0", - "lodash": "^4.17.20", - "marked": "^4.0.10", - "md5": "^2.2.1", - "pify": "^4.0.0", - "require-subvert": "^0.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@qxip/to-file": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@qxip/to-file/-/to-file-0.2.1.tgz", - "integrity": "sha512-d+MSLeFzdhPfqoB+dLPBPDqsmJ4tqOCy5wmMBpNvqp6EvsJFd9p7HQjBnUHjVaXoVNk8BWX/AlqA0RM2RMj8GQ==", - "dependencies": { - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "file-contents": "^0.2.4", - "glob-parent": "^6.0.2", - "is-valid-glob": "^0.3.0", - "isobject": "^2.1.0", - "lazy-cache": "^2.0.1", - "vinyl": "^1.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@qxip/to-file/node_modules/file-contents": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/file-contents/-/file-contents-0.2.4.tgz", - "integrity": "sha512-PEz7U6YlXr+dvWCtW63DUY1LUTHOVs1rv4s1/I/39dpvvidQqMSTY6JklazQS60MMoI/ztpo5kMlpdvGagvLbA==", - "dependencies": { - "extend-shallow": "^2.0.0", - "file-stat": "^0.1.0", - "graceful-fs": "^4.1.2", - "is-buffer": "^1.1.0", - "is-utf8": "^0.2.0", - "lazy-cache": "^0.2.3", - "through2": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@qxip/to-file/node_modules/file-contents/node_modules/lazy-cache": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", - "integrity": "sha512-gkX52wvU/R8DVMMt78ATVPFMJqfW8FPz1GZ1sVHBVQHmu/WvhIWE4cE1GBzhJNFicDeYhnwp6Rl35BcAIM3YOQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@qxip/to-file/node_modules/file-stat": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/file-stat/-/file-stat-0.1.3.tgz", - "integrity": "sha512-f72m4132aOd5DVtREdDX8I0Dd7Zf/3PiUYYvn4BFCxfsLqj6r8joBZzrRlfvsNvxhADw+jpEa0AnWPII9H0Fbg==", - "dependencies": { - "graceful-fs": "^4.1.2", - "lazy-cache": "^0.2.3", - "through2": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@qxip/to-file/node_modules/file-stat/node_modules/lazy-cache": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", - "integrity": "sha512-gkX52wvU/R8DVMMt78ATVPFMJqfW8FPz1GZ1sVHBVQHmu/WvhIWE4cE1GBzhJNFicDeYhnwp6Rl35BcAIM3YOQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@stricjs/router": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@stricjs/router/-/router-5.0.6.tgz", - "integrity": "sha512-PfoGCj8H29UjJ4nBlvzgnvLtX4xYNoe+pWA09yPzQqDuwBApQWFTpSEKLWKzhPJpYjLvpclfBK9Js9xpTTJhEw==" - }, - "node_modules/@stricjs/utils": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@stricjs/utils/-/utils-1.6.1.tgz", - "integrity": "sha512-dslPWhGMNLN+gYW/TkpCiTCgmFQGRhVdZV122sFL0oxS2WuqGDQL51bEZOj/RmONamjIdlSWzNQecexix44NOA==" - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "22.5.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", - "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true - }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz", - "integrity": "sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/abstract-logging": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", - "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" - }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-green": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-green/-/ansi-green-0.1.1.tgz", - "integrity": "sha512-WJ70OI4jCaMy52vGa/ypFSKFb/TrYNPaQ2xco5nUwE0C5H8piume/uAZNNdXXiMQ6DbRmiE7l8oNBHu05ZKkrw==", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ansi-wrap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/args": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz", - "integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==", - "dev": true, - "dependencies": { - "camelcase": "5.0.0", - "chalk": "2.4.2", - "leven": "2.1.0", - "mri": "1.1.4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/args/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/args/node_modules/camelcase": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", - "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/args/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/args/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/args/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/args/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/args/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/args/node_modules/leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/args/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", - "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/async-array-reduce": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/async-array-reduce/-/async-array-reduce-0.2.1.tgz", - "integrity": "sha512-/ywTADOcaEnwiAnOEi0UB/rAcIq5bTFfCV9euv3jLYFUMmy6KvKccTQUnLlp8Ensmfj43wHSmbGiPqjsZ6RhNA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/async-each": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz", - "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/atomic-sleep": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/avvio": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.4.0.tgz", - "integrity": "sha512-CDSwaxINFy59iNwhYnkvALBwZiTydGkOecZyPkqBpABYR1KqGEsET0VOOYDwtleZSUIdeY36DC2bSZ24CO1igA==", - "dependencies": { - "@fastify/error": "^3.3.0", - "fastq": "^1.17.1" - } - }, - "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/b4a": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", - "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==" - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "bin": { - "babylon": "bin/babylon.js" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bintrees": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", - "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==" - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "node_modules/bnf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bnf/-/bnf-1.0.1.tgz", - "integrity": "sha512-BP7aFTqqGrVSkO4sZwYKizhfJQoSWncLk/xP2zRFmdzm6vedJ8aSUP/+yZecEb3HcjygaC56dXSr34pxETYuGQ==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", - "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001657", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001657.tgz", - "integrity": "sha512-DPbJAlP8/BAXy3IgiWmZKItubb3TYGP0WscQQlVGIfT4s/YlFYVuJgyOsQNP7rJRChx/qdMeLJQJP0Sgg2yjNA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/casual": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/casual/-/casual-1.6.2.tgz", - "integrity": "sha512-NQObL800rg32KZ9bBajHbyDjxLXxxuShChQg7A4tbSeG3n1t7VYGOSkzFSI9gkSgOHp+xilEJ7G0L5l6M30KYA==", - "dev": true, - "dependencies": { - "mersenne-twister": "^1.0.1", - "moment": "^2.15.2" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "engines": { - "node": "*" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.0.tgz", - "integrity": "sha512-N1NGmowPlGBLsOZLPvm48StN04V4YvQRL0i6b7ctrVY3epjP/ct7hFLOItz6pDIvRjwpfPxi52a2UWV2ziir8g==", - "dev": true - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha512-dhUqc57gSMCo6TX85FLfe51eC/s+Im2MLkAgJwfaRRexR2tA4dd3eLEW4L6efzHc2iNorrRRXITifnDLlRrhaA==" - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "engines": { - "node": "*" - } - }, - "node_modules/csv-writer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/csv-writer/-/csv-writer-1.6.0.tgz", - "integrity": "sha512-NOx7YDFWEsM/fTRAJjRpPp8t+MKRVvniAg9wQlUKx20MFrPs73WLJhFf5iteqrxNYnsy924K3Iroh3yNHeYd2g==" - }, - "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "dependencies": { - "@babel/runtime": "^7.21.0" - }, - "engines": { - "node": ">=0.11" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" - } - }, - "node_modules/dateformat": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", - "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dedent": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", - "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", - "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dependencies": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "node_modules/easy-factory": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/easy-factory/-/easy-factory-1.3.0.tgz", - "integrity": "sha512-PRY7E0JgyvoEKF/Z3jHK0NM2lru67vqgdLoi1D4R4JJNiT8AE3l9ozvy0N8SsztqHGAGLA3XEbnNfPc2scW4oA==" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.14", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.14.tgz", - "integrity": "sha512-bEfPECb3fJ15eaDnu9LEJ2vPGD6W1vt7vZleSVyFhYuMIKm3vz/g9lt7IvEzgdwj58RjbPKUF2rXTCN/UW47tQ==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/enquirer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", - "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-standard": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", - "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "peerDependencies": { - "eslint": "^7.12.1", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.2.1 || ^5.0.0" - } - }, - "node_modules/eslint-config-standard-jsx": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-10.0.0.tgz", - "integrity": "sha512-hLeA2f5e06W1xyr/93/QJulN/rLbUVUmqTlexv9PRKHFwEC9ffJcH2LvJhMoEqYQBEYafedgGZXH2W8NUpt5lA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "peerDependencies": { - "eslint": "^7.12.1", - "eslint-plugin-react": "^7.21.5" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.9.0.tgz", - "integrity": "sha512-McVbYmwA3NEKwRQY5g4aWMdcZE5xZxV8i8l7CqJSrameuGSQJtSWaL/LxTEzSKKaCcOhlpDR8XEfYXWPrdo/ZQ==", - "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", - "dev": true, - "dependencies": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" - }, - "engines": { - "node": ">=8.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=4.19.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz", - "integrity": "sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==", - "dev": true, - "dependencies": { - "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.8", - "array.prototype.findlastindex": "^1.2.5", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.9.0", - "hasown": "^2.0.2", - "is-core-module": "^2.15.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "object.groupby": "^1.0.3", - "object.values": "^1.2.0", - "semver": "^6.3.1", - "tsconfig-paths": "^3.15.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-jest": { - "version": "25.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", - "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/experimental-utils": "^5.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "jest": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", - "dev": true, - "dependencies": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" - }, - "engines": { - "node": ">=8.10.0" - }, - "peerDependencies": { - "eslint": ">=5.16.0" - } - }, - "node_modules/eslint-plugin-node/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/eslint-plugin-node/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-promise": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.2.0.tgz", - "integrity": "sha512-SftLb1pUG01QYq2A/hGAWfDRXqYD82zE7j7TopDOyNdU+7SvvoXREls/+PRTY17vUXzXnZA/zfnyKgRH6x4JJw==", - "dev": true, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "peerDependencies": { - "eslint": "^7.0.0" - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.25.3.tgz", - "integrity": "sha512-ZMbFvZ1WAYSZKY662MBVEWR45VaBT6KSJCiupjrNlcdakB90juaZeDCbJq19e73JZQubqFtgETohwgAt8u5P6w==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.3", - "array.prototype.flatmap": "^1.2.4", - "doctrine": "^2.1.0", - "estraverse": "^5.2.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.0.4", - "object.entries": "^1.1.4", - "object.fromentries": "^2.0.4", - "object.hasown": "^1.0.0", - "object.values": "^1.1.4", - "prop-types": "^15.7.2", - "resolve": "^2.0.0-next.3", - "string.prototype.matchall": "^4.0.5" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7" - } - }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expand-tilde": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", - "integrity": "sha512-rtmc+cjLZqnu9dSYosX9EWmSJhTwpACgJQTfj4hgg2JjOD/6SIQalZrt4a3aQeh++oNxkazcaxrhPUj6+g5G/Q==", - "dependencies": { - "os-homedir": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-content-type-parse": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", - "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==" - }, - "node_modules/fast-decode-uri-component": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", - "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-json-stringify": { - "version": "5.16.1", - "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.16.1.tgz", - "integrity": "sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==", - "dependencies": { - "@fastify/merge-json-schemas": "^0.1.0", - "ajv": "^8.10.0", - "ajv-formats": "^3.0.1", - "fast-deep-equal": "^3.1.3", - "fast-uri": "^2.1.0", - "json-schema-ref-resolver": "^1.0.1", - "rfdc": "^1.2.0" - } - }, - "node_modules/fast-json-stringify/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/fast-json-stringify/node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/fast-json-stringify/node_modules/ajv/node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" - }, - "node_modules/fast-json-stringify/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fast-querystring": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", - "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", - "dependencies": { - "fast-decode-uri-component": "^1.0.1" - } - }, - "node_modules/fast-redact": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", - "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "dev": true - }, - "node_modules/fast-uri": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.4.0.tgz", - "integrity": "sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==" - }, - "node_modules/fastify": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.28.1.tgz", - "integrity": "sha512-kFWUtpNr4i7t5vY2EJPCN2KgMVpuqfU4NjnJNCgiNB900oiDeYqaNDRcAfeBbOF5hGixixxcKnOU4KN9z6QncQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "dependencies": { - "@fastify/ajv-compiler": "^3.5.0", - "@fastify/error": "^3.4.0", - "@fastify/fast-json-stringify-compiler": "^4.3.0", - "abstract-logging": "^2.0.1", - "avvio": "^8.3.0", - "fast-content-type-parse": "^1.1.0", - "fast-json-stringify": "^5.8.0", - "find-my-way": "^8.0.0", - "light-my-request": "^5.11.0", - "pino": "^9.0.0", - "process-warning": "^3.0.0", - "proxy-addr": "^2.0.7", - "rfdc": "^1.3.0", - "secure-json-parse": "^2.7.0", - "semver": "^7.5.4", - "toad-cache": "^3.3.0" - } - }, - "node_modules/fastify-metrics": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/fastify-metrics/-/fastify-metrics-10.6.0.tgz", - "integrity": "sha512-QIPncCnwBOEObMn+VaRhsBC1ox8qEsaiYF2sV/A1UbXj7ic70W8/HNn/hlEC2W8JQbBeZMx++o1um2fPfhsFDQ==", - "dependencies": { - "fastify-plugin": "^4.3.0", - "prom-client": "^14.2.0" - }, - "peerDependencies": { - "fastify": ">=4" - } - }, - "node_modules/fastify-plugin": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-4.5.1.tgz", - "integrity": "sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==" - }, - "node_modules/fastify/node_modules/on-exit-leak-free": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", - "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/fastify/node_modules/pino": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-9.4.0.tgz", - "integrity": "sha512-nbkQb5+9YPhQRz/BeQmrWpEknAaqjpAqRK8NwJpmrX/JHu7JuZC5G1CeAwJDJfGes4h+YihC6in3Q2nGb+Y09w==", - "dependencies": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^1.2.0", - "pino-std-serializers": "^7.0.0", - "process-warning": "^4.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.2.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^4.0.1", - "thread-stream": "^3.0.0" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/fastify/node_modules/pino-abstract-transport": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", - "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", - "dependencies": { - "readable-stream": "^4.0.0", - "split2": "^4.0.0" - } - }, - "node_modules/fastify/node_modules/pino-std-serializers": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", - "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==" - }, - "node_modules/fastify/node_modules/pino/node_modules/process-warning": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.0.tgz", - "integrity": "sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==" - }, - "node_modules/fastify/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/fastify/node_modules/real-require": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", - "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/fastify/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/fastify/node_modules/sonic-boom": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.1.0.tgz", - "integrity": "sha512-NGipjjRicyJJ03rPiZCJYjwlsuP2d1/5QUviozRXC7S3WdVWNK5e3Ojieb9CCyfhq2UC+3+SRd9nG3I2lPRvUw==", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/fastify/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/fastify/node_modules/thread-stream": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", - "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", - "dependencies": { - "real-require": "^0.2.0" - } - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-contents": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/file-contents/-/file-contents-0.3.2.tgz", - "integrity": "sha512-7xaJjA+9eTve2l1FzoagBX26tICgaTwLPAY9vi/FDutEUKNeBR4YYvvQ8bgxuYJb09edaAQoEGIa6Juim88dpQ==", - "dependencies": { - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "file-stat": "^0.2.3", - "fs-exists-sync": "^0.1.0", - "graceful-fs": "^4.1.4", - "is-buffer": "^1.1.3", - "isobject": "^2.1.0", - "lazy-cache": "^2.0.1", - "strip-bom-buffer": "^0.1.1", - "strip-bom-string": "^0.1.2", - "through2": "^2.0.1", - "vinyl": "^1.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-stat": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/file-stat/-/file-stat-0.2.3.tgz", - "integrity": "sha512-wjHoKZzas90Jl1XOBfLnNGc5gl9JTm7sTceuoO4P3OdadlCz1ELrOxYmiamqLJP4S8+phD7wzW8S1oBj+8vnBQ==", - "dependencies": { - "fs-exists-sync": "^0.1.0", - "graceful-fs": "^4.1.4", - "lazy-cache": "^2.0.1", - "through2": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-my-way": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.2.2.tgz", - "integrity": "sha512-Dobi7gcTEq8yszimcfp/R7+owiT4WncAJ7VTTgFH1jYJ5GaG1FbhjwDG820hptN0QDFvzVY3RfCzdInvGPGzjA==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-querystring": "^1.0.0", - "safe-regex2": "^3.1.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-yarn-workspace-root": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", - "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", - "dependencies": { - "micromatch": "^4.0.2" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" - }, - "node_modules/flow-copy-source": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/flow-copy-source/-/flow-copy-source-2.0.9.tgz", - "integrity": "sha512-7zX/oHSIHe8YRGiA9QIcC4SW6KF667ikdmiDfbST15up1Ona8dn7Xy0PmSrfw6ceBWDww8sRKlCLKsztStpYkQ==", - "dependencies": { - "chokidar": "^3.0.0", - "fs-extra": "^8.1.0", - "glob": "^7.0.0", - "kefir": "^3.7.3", - "yargs": "^15.0.1" - }, - "bin": { - "flow-copy-source": "bin/flow-copy-source.js" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/flow-copy-source/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/flow-copy-source/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flow-copy-source/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - }, - "node_modules/flow-copy-source/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flow-copy-source/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/flow-remove-types": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/flow-remove-types/-/flow-remove-types-1.2.3.tgz", - "integrity": "sha512-ypq/U3V+t9atYiOuSJd40tekCra03EHKoRsiK/wXGrsZimuum0kdwVY7Yv0HTaoXgHW1WiayomYd+Q3kkvPl9Q==", - "dependencies": { - "babylon": "^6.15.0", - "vlq": "^0.2.1" - }, - "bin": { - "flow-node": "flow-node", - "flow-remove-types": "flow-remove-types" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.8.tgz", - "integrity": "sha512-xgrmBhBToVKay1q2Tao5LI26B83UhrB/vM1avwVSDzt8rx3rO6AizBAaF46EgksTVr+rFTQaqZZ9MVBfUe4nig==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", - "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "node_modules/fs-exists-sync": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", - "integrity": "sha512-cR/vflFyPZtrN6b38ZyWxpWdhlXrzZEBawlpBQMq7033xVY7/kg0GDMBK5jg8lDYQckdJ5x/YC88lM3C7VMsLg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/generate-function": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", - "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", - "dependencies": { - "is-property": "^1.0.2" - } - }, - "node_modules/generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha512-TuOwZWgJ2VAMEGJvAyPWvpqxSANF0LDpmyHauMjFYzaACvn+QTT/AZomvPCzVBV7yDN3OmwHQ5OvHaeLKre3JQ==", - "dependencies": { - "is-property": "^1.0.0" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/global-modules": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", - "integrity": "sha512-JeXuCbvYzYXcwE6acL9V2bAOeSIGl4dD+iwLY9iUx2VBJJ80R18HCn+JCwHM9Oegdfya3lEkGCdaRkSyc10hDA==", - "dependencies": { - "global-prefix": "^0.1.4", - "is-windows": "^0.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/global-prefix": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz", - "integrity": "sha512-gOPiyxcD9dJGCEArAhF4Hd0BAqvAe/JzERP7tYumE4yIkmIedPUVXcJFWbV3/p/ovIIvKjkrTk+f1UVkq7vvbw==", - "dependencies": { - "homedir-polyfill": "^1.0.0", - "ini": "^1.3.4", - "is-windows": "^0.2.0", - "which": "^1.2.12" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/google-protobuf": { - "version": "3.21.4", - "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz", - "integrity": "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==" - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/handlebars-utils": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/handlebars-utils/-/handlebars-utils-1.0.6.tgz", - "integrity": "sha512-d5mmoQXdeEqSKMtQQZ9WkiUcO1E3tPbWxluCK9hVgIDPzQa9WsKo3Lbe/sGflTe7TomHEeZaOgwIkyIr1kfzkw==", - "dependencies": { - "kind-of": "^6.0.0", - "typeof-article": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/handlebars-utils/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", - "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-glob": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/has-glob/-/has-glob-0.1.1.tgz", - "integrity": "sha512-WMHzb7oCwDcMDngWy0b+viLjED8zvSi5d4/YdBetADHX/rLH+noJaRTytuyN6thTxxM7lK+FloogQHHdOOR+7g==", - "dependencies": { - "is-glob": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-glob/node_modules/is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-glob/node_modules/is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", - "dependencies": { - "is-extglob": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hash-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hash-string/-/hash-string-1.0.0.tgz", - "integrity": "sha512-dtNNyxXobzHavayZwOwRWhBTqS9GX4jDjIMsGc0fDyaN2A+4zMn5Ua9ODDCggN6w3Spma6mAHL3ImmW3BkWDmQ==" - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dependencies": { - "parse-passwd": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/hpagent": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-1.2.0.tgz", - "integrity": "sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/into-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", - "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", - "dependencies": { - "from2": "^2.3.0", - "p-is-promise": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-absolute": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz", - "integrity": "sha512-7Kr05z5LkcOpoMvxHN1PC11WbPabdNFmMYYo0eZvWu3BfVS0T03yoqYDczoCBx17xqk2x1XAZrcKiFVL88jxlQ==", - "dependencies": { - "is-relative": "^0.2.1", - "is-windows": "^0.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-accessor-descriptor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz", - "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==", - "dependencies": { - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dependencies": { - "ci-info": "^2.0.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-ci/node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - }, - "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", - "dev": true, - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-descriptor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz", - "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==", - "dependencies": { - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", - "dev": true, - "dependencies": { - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-descriptor": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", - "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-relative": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz", - "integrity": "sha512-9AMzjRmLqcue629b4ezEVSK6kJsYJlUIhMcygmYORUgwUNJiavHcC3HkaGx0XYpyVKQSOqFbMEZmW42cY87sYw==", - "dependencies": { - "is-unc-path": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dev": true, - "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-unc-path": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz", - "integrity": "sha512-HhLc5VDMH4pu3oMtIuunz/DFQUIoR561kMME3U3Afhj8b7vH085vkIkemrz1kLXCEIuoMAmO3yVmafWdSbGW8w==", - "dependencies": { - "unc-path-regex": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==" - }, - "node_modules/is-valid-glob": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz", - "integrity": "sha512-CvG8EtJZ8FyzVOGPzrDorzyN65W1Ld8BVnqshRCah6pFIsprGx3dKgFtjLn/Vw9kGqR4OlR84U7yhT9ZVTyWIQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-windows": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", - "integrity": "sha512-n67eJYmXbniZB7RF4I/FTjK1s6RPOCTxhYrVYLRaCt3lF0mpWZPKr3T2LSZAqyjQsxR2qMmGYXXzK0YWwcPM1Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", - "dependencies": { - "isarray": "1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-ref-resolver": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", - "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - } - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz", - "integrity": "sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg==", - "dependencies": { - "call-bind": "^1.0.5", - "isarray": "^2.0.5", - "jsonify": "^0.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json-stable-stringify/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonic": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/jsonic/-/jsonic-1.0.1.tgz", - "integrity": "sha512-6GitEN4plTuB/I1o9kDZl7Pgc+DvFG1BG88IqaUz4eQglCA1uAgxWdXhLNA6ffaYsmzPjOysDpp6CYTwRiuXLw==" - }, - "node_modules/jsonify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", - "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/kefir": { - "version": "3.8.8", - "resolved": "https://registry.npmjs.org/kefir/-/kefir-3.8.8.tgz", - "integrity": "sha512-xWga7QCZsR2Wjy2vNL3Kq/irT+IwxwItEWycRRlT5yhqHZK2fmEhziP+LzcJBWSTAMranGKtGTQ6lFpyJS3+jA==" - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/klaw-sync": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", - "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", - "dependencies": { - "graceful-fs": "^4.1.11" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/lazy-cache": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", - "integrity": "sha512-7vp2Acd2+Kz4XkzxGxaB1FWOi8KjWIWsgdfD5MCb86DWvlLqhRPM+d6Pro3iNEL5VT9mstz5hKAlcd+QR6H3aA==", - "dependencies": { - "set-getter": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/light-my-request": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.14.0.tgz", - "integrity": "sha512-aORPWntbpH5esaYpGOOmri0OHDOe3wC5M2MQxZ9dvMLZm6DnaAn0kJlcbU9hwsQgLzmZyReKwFwwPkR+nHu5kA==", - "dependencies": { - "cookie": "^0.7.0", - "process-warning": "^3.0.0", - "set-cookie-parser": "^2.4.1" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/load-json-file": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", - "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.15", - "parse-json": "^4.0.0", - "pify": "^4.0.1", - "strip-bom": "^3.0.0", - "type-fest": "^0.3.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/type-fest": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", - "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true - }, - "node_modules/log-ok": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/log-ok/-/log-ok-0.1.1.tgz", - "integrity": "sha512-cc8VrkS6C+9TFuYAwuHpshrcrGRAv7d0tUJ0GdM72ZBlKXtlgjUZF84O+OhQUdiVHoF7U/nVxwpjOdwUJ8d3Vg==", - "dependencies": { - "ansi-green": "^0.1.1", - "success-symbol": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/logfmt": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/logfmt/-/logfmt-1.4.0.tgz", - "integrity": "sha512-p1Ow0C2dDJYaQBhRHt+HVMP6ELuBm4jYSYNHPMfz0J5wJ9qA6/7oBOlBZBfT1InqguTYcvJzNea5FItDxTcbyw==", - "dependencies": { - "split": "0.2.x", - "through": "2.3.x" - }, - "bin": { - "logfmt": "bin/logfmt" - } - }, - "node_modules/long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/matched": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/matched/-/matched-0.4.4.tgz", - "integrity": "sha512-zpasnbB5vQkvb0nfcKV0zEoGgMtV7atlWR1Vk3E8tEKh6EicMseKtVV+5vc+zsZwvDlcNMKlKK/CVOEeAalYRQ==", - "dependencies": { - "arr-union": "^3.1.0", - "async-array-reduce": "^0.2.0", - "extend-shallow": "^2.0.1", - "fs-exists-sync": "^0.1.0", - "glob": "^7.0.5", - "has-glob": "^0.1.1", - "is-valid-glob": "^0.3.0", - "lazy-cache": "^2.0.1", - "resolve-dir": "^0.1.0" - }, - "engines": { - "node": ">= 0.12.0" - } - }, - "node_modules/md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "dependencies": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/mersenne-twister": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mersenne-twister/-/mersenne-twister-1.1.0.tgz", - "integrity": "sha512-mUYWsMKNrm4lfygPkL3OfGzOPTR2DBlTkBNHM//F6hGp8cLThY897crAlk3/Jo17LEOOjQUrNAx6DvgO77QJkA==", - "dev": true - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/mime-db": { - "version": "1.53.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", - "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mnemonist": { - "version": "0.39.6", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.6.tgz", - "integrity": "sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==", - "dependencies": { - "obliterator": "^2.0.1" - } - }, - "node_modules/module-details-from-path": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", - "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==", - "dev": true - }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/mri": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", - "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "node_modules/node-abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", - "dev": true - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-gzip": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/node-gzip/-/node-gzip-1.1.2.tgz", - "integrity": "sha512-ZB6zWpfZHGtxZnPMrJSKHVPrRjURoUzaDbLFj3VO70mpLTW5np96vXyHwft4Id0o+PYIzgDkBUjIzaNHhQ8srw==" - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.hasown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obliterator": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", - "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==" - }, - "node_modules/on-exit-leak-free": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz", - "integrity": "sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==" - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", - "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-is-promise": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", - "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/pako": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", - "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" - }, - "node_modules/papaparse": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", - "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/patch-package": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.5.1.tgz", - "integrity": "sha512-I/4Zsalfhc6bphmJTlrLoOcAF87jcxko4q0qsv4bGcurbr8IskEOtdnt9iCmsQVGL1B+iUhSQqweyTLJfCF9rA==", - "dependencies": { - "@yarnpkg/lockfile": "^1.1.0", - "chalk": "^4.1.2", - "cross-spawn": "^6.0.5", - "find-yarn-workspace-root": "^2.0.0", - "fs-extra": "^9.0.0", - "is-ci": "^2.0.0", - "klaw-sync": "^6.0.0", - "minimist": "^1.2.6", - "open": "^7.4.2", - "rimraf": "^2.6.3", - "semver": "^5.6.0", - "slash": "^2.0.0", - "tmp": "^0.0.33", - "yaml": "^1.10.2" - }, - "bin": { - "patch-package": "index.js" - }, - "engines": { - "node": ">=10", - "npm": ">5" - } - }, - "node_modules/patch-package/node_modules/cross-spawn": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/patch-package/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/patch-package/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/patch-package/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/patch-package/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/patch-package/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/patch-package/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/patch-package/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/patch-package/node_modules/slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/patch-package/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/patch-package/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/peek-stream": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz", - "integrity": "sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==", - "dependencies": { - "buffer-from": "^1.0.0", - "duplexify": "^3.5.0", - "through2": "^2.0.3" - } - }, - "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/pino": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-7.11.0.tgz", - "integrity": "sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==", - "dependencies": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.0.0", - "on-exit-leak-free": "^0.2.0", - "pino-abstract-transport": "v0.5.0", - "pino-std-serializers": "^4.0.0", - "process-warning": "^1.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.1.0", - "safe-stable-stringify": "^2.1.0", - "sonic-boom": "^2.2.1", - "thread-stream": "^0.15.1" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/pino-abstract-transport": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz", - "integrity": "sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==", - "dependencies": { - "duplexify": "^4.1.2", - "split2": "^4.0.0" - } - }, - "node_modules/pino-abstract-transport/node_modules/duplexify": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", - "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", - "dependencies": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.2" - } - }, - "node_modules/pino-abstract-transport/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pino-pretty": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-7.6.1.tgz", - "integrity": "sha512-H7N6ZYkiyrfwBGW9CSjx0uyO9Q2Lyt73881+OTYk8v3TiTdgN92QHrWlEq/LeWw5XtDP64jeSk3mnc6T+xX9/w==", - "dev": true, - "dependencies": { - "args": "^5.0.1", - "colorette": "^2.0.7", - "dateformat": "^4.6.3", - "fast-safe-stringify": "^2.0.7", - "joycon": "^3.1.1", - "on-exit-leak-free": "^0.2.0", - "pino-abstract-transport": "^0.5.0", - "pump": "^3.0.0", - "readable-stream": "^3.6.0", - "rfdc": "^1.3.0", - "secure-json-parse": "^2.4.0", - "sonic-boom": "^2.2.0", - "strip-json-comments": "^3.1.1" - }, - "bin": { - "pino-pretty": "bin.js" - } - }, - "node_modules/pino-pretty/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pino-std-serializers": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz", - "integrity": "sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==" - }, - "node_modules/pino/node_modules/process-warning": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", - "integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==" - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-conf": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz", - "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==", - "dev": true, - "dependencies": { - "find-up": "^3.0.0", - "load-json-file": "^5.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-conf/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha512-fjAPuiws93rm7mPUu21RdBnkeZNrbfCFCwfAhPWY+rR3zG0ubpe5cEReHOw5fIbfmsxEV/g2kSxGTATY3Bpnwg==", - "dev": true, - "dependencies": { - "find-up": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-up/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-up/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-up/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-up/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-up/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/plugnplay": { - "name": "@qxip/plugnplay", - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@qxip/plugnplay/-/plugnplay-3.3.1.tgz", - "integrity": "sha512-Y/MoisMGHwgtcV9qjyOERhPTqJsdBaPXEdLK0i6U5cntygXWeX74Em0ttWcIawy1bO9w2uhxc2WhfB/p9Ex1bQ==", - "dependencies": { - "@qxip/copy": "^0.3.4", - "easy-factory": "^1.3.0", - "flatted": "^3.0.5", - "flow-copy-source": "^2.0.9", - "flow-remove-types": "^1.2.3", - "glob": "^7.1.2", - "js-yaml": "^3.14.0", - "lodash": "^4.17.20", - "marked": "^4.0.10", - "md5": "^2.2.1", - "pify": "^4.0.0", - "require-subvert": "^0.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/process-warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", - "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/prom-client": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-14.2.0.tgz", - "integrity": "sha512-sF308EhTenb/pDRPakm+WgiN+VdM/T1RaHj1x+MvAuT8UiQP8JmOEbxVqtkbfR4LrvOg5n7ic01kRBDGXjYikA==", - "dependencies": { - "tdigest": "^0.1.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/prometheus-remote-write": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/prometheus-remote-write/-/prometheus-remote-write-0.3.1.tgz", - "integrity": "sha512-BnaULBmeh5Tmo5XwQCCJokI6yFyFXsBpBo9ztjGr4aES4pwIkYf1WuHw1Utm6cqcoYQNUxMScMflIkoj6k+2aQ==", - "dev": true, - "dependencies": { - "protobufjs": "^7.2.4", - "snappyjs": "^0.6.1" - }, - "peerDependencies": { - "node-fetch": "^2.6.7" - }, - "peerDependenciesMeta": { - "node-fetch": { - "optional": true - } - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, - "node_modules/protobufjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", - "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", - "hasInstallScript": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/protocol-buffers": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/protocol-buffers/-/protocol-buffers-4.2.0.tgz", - "integrity": "sha512-hNp56d5uuREVde7UqP+dmBkwzxrhJwYU5nL/mdivyFfkRZdgAgojkyBeU3jKo7ZHrjdSx6Q1CwUmYJI6INt20g==", - "dependencies": { - "generate-function": "^2.0.0", - "generate-object-property": "^1.2.0", - "protocol-buffers-encodings": "^1.1.0", - "protocol-buffers-schema": "^3.1.1", - "signed-varint": "^2.0.0", - "varint": "^5.0.0" - }, - "bin": { - "protocol-buffers": "bin.js" - } - }, - "node_modules/protocol-buffers-encodings": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/protocol-buffers-encodings/-/protocol-buffers-encodings-1.2.0.tgz", - "integrity": "sha512-daeNPuKh1NlLD1uDfbLpD+xyUTc07nEtfHwmBZmt/vH0B7VOM+JOCOpDcx9ZRpqHjAiIkGqyTDi+wfGSl17R9w==", - "dependencies": { - "b4a": "^1.6.0", - "signed-varint": "^2.0.1", - "varint": "5.0.0" - } - }, - "node_modules/protocol-buffers-encodings/node_modules/varint": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.0.tgz", - "integrity": "sha512-gC13b/bWrqQoKY2EmROCZ+AR0jitc6DnDGaQ6Ls9QpKmuSgJB1eQ7H3KETtQm7qSdMWMKCmsshyCmUwMLh3OAA==" - }, - "node_modules/protocol-buffers-schema": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", - "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/pumpify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", - "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", - "dependencies": { - "duplexify": "^4.1.1", - "inherits": "^2.0.3", - "pump": "^3.0.0" - } - }, - "node_modules/pumpify/node_modules/duplexify": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", - "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", - "dependencies": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.2" - } - }, - "node_modules/pumpify/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-format-unescaped": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", - "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true - }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dev": true, - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", - "dev": true, - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg/node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/real-require": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz", - "integrity": "sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/record-cache": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/record-cache/-/record-cache-1.2.0.tgz", - "integrity": "sha512-kyy3HWCez2WrotaL3O4fTn0rsIdfRKOdQQcEJ9KpvmKmbffKVvwsloX063EgRUlpJIXHiDQFhJcTbZequ2uTZw==", - "dependencies": { - "b4a": "^1.3.1" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha512-AFBWBy9EVRTa/LhEcG8QDP3FvpwZqmvN2QFDuJswFeaVhWnZMp8q3E6Zd90SR04PlIwfGdyVjNyLPyen/ek5CQ==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-in-the-middle": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.2.0.tgz", - "integrity": "sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "module-details-from-path": "^1.0.3", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, - "node_modules/require-subvert": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/require-subvert/-/require-subvert-0.1.0.tgz", - "integrity": "sha512-Cmwwc1PD7AI0wGecoTjsy04vNcgtvveCBrz271HUAyi9PT02ejYLQ6er74vn6qPFUFqX+TzJk9QBt7GbtoK/YQ==", - "deprecated": "no longer maintained" - }, - "node_modules/rereadable-stream": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/rereadable-stream/-/rereadable-stream-1.4.14.tgz", - "integrity": "sha512-vGANaSU3Uvl33Lz88otjkoXkiBx01KjrusdsW2K95JbJveWZdjf11CyutMRZyy7nj7NyCTRynauTbaiM7MCgkg==", - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-dir": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", - "integrity": "sha512-QxMPqI6le2u0dCLyiGzgy92kjkkL6zO0XyvHzjdTNH3zM6e5Hz3BwG6+aEyNgiQ5Xz6PwTwgQEj3U50dByPKIA==", - "dependencies": { - "expand-tilde": "^1.2.2", - "global-modules": "^0.2.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ret": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.4.3.tgz", - "integrity": "sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-array-concat/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex2": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-3.1.0.tgz", - "integrity": "sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug==", - "dependencies": { - "ret": "~0.4.0" - } - }, - "node_modules/safe-stable-stringify": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", - "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/scramjet": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/scramjet/-/scramjet-4.37.0.tgz", - "integrity": "sha512-Y6b59qGsulkr5MxiVn9CABnL9pE/sPKihCcWSUhzZc6W0YWbfLWRXc1fE1M40QKfOQUBxks81efzJ7WpEuFmlQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/scramjetorg" - }, - { - "type": "individual", - "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7F7V65C43EBMW" - } - ], - "dependencies": { - "papaparse": "^5.4.1", - "rereadable-stream": "^1.4.14", - "scramjet-core": "^4.32.12" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/scramjet-core": { - "version": "4.32.12", - "resolved": "https://registry.npmjs.org/scramjet-core/-/scramjet-core-4.32.12.tgz", - "integrity": "sha512-FkNaZqzXvzqdwrUWzMztJq2RUBcpBlm08zOYIhA69+//FzgrespLBz7DmCXdXfujjvmUIFGgq/T3aPFy1ctonw==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/secure-json-parse": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", - "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" - }, - "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, - "node_modules/set-cookie-parser": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.0.tgz", - "integrity": "sha512-lXLOiqpkUumhRdFF3k1osNXCy9akgx/dyPZ5p8qAg9seJzXr5ZrlqZuWIMuY6ejOsVLE6flJ5/h3lsn57fQ/PQ==" - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-getter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.1.tgz", - "integrity": "sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw==", - "dependencies": { - "to-object-path": "^0.3.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/shimmer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", - "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==", - "dev": true - }, - "node_modules/short-hash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/short-hash/-/short-hash-1.0.0.tgz", - "integrity": "sha512-qbUCD2Pkl4IXRyVqneEjGnUr0NGDGLzZnBUVGJngIQZf/FrhOL0yJhH+JQzak0t8xMmScIKpoX1SxOsPHdwa4w==", - "dependencies": { - "hash-string": "^1.0.0" - } - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/signed-varint": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/signed-varint/-/signed-varint-2.0.1.tgz", - "integrity": "sha512-abgDPg1106vuZZOvw7cFwdCABddfJRz5akcCcchzTbhyhYnsG31y4AlZEgp315T7W3nQq5P4xeOm186ZiPVFzw==", - "dependencies": { - "varint": "~5.0.0" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/snappyjs": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/snappyjs/-/snappyjs-0.6.1.tgz", - "integrity": "sha512-YIK6I2lsH072UE0aOFxxY1dPDCS43I5ktqHpeAsuLNYWkE5pGxRGWfDM4/vSUfNzXjC1Ivzt3qx31PCLmc9yqg==" - }, - "node_modules/sonic-boom": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.8.0.tgz", - "integrity": "sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.20", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", - "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", - "dev": true - }, - "node_modules/split": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz", - "integrity": "sha512-e0pKq+UUH2Xq/sXbYpZBZc3BawsfDZ7dgv+JtRTUPNcvF5CMR4Y9cvJqkMY0MoxWzTHvZuz1beg6pNEKlszPiQ==", - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/standard": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/standard/-/standard-16.0.4.tgz", - "integrity": "sha512-2AGI874RNClW4xUdM+bg1LRXVlYLzTNEkHmTG5mhyn45OhbgwA+6znowkOGYy+WMb5HRyELvtNy39kcdMQMcYQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "eslint": "~7.18.0", - "eslint-config-standard": "16.0.3", - "eslint-config-standard-jsx": "10.0.0", - "eslint-plugin-import": "~2.24.2", - "eslint-plugin-node": "~11.1.0", - "eslint-plugin-promise": "~5.1.0", - "eslint-plugin-react": "~7.25.1", - "standard-engine": "^14.0.1" - }, - "bin": { - "standard": "bin/cmd.js" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/standard-engine": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-14.0.1.tgz", - "integrity": "sha512-7FEzDwmHDOGva7r9ifOzD3BGdTbA7ujJ50afLVdW/tK14zQEptJjbFuUfn50irqdHDcTbNh0DTIoMPynMCXb0Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "get-stdin": "^8.0.0", - "minimist": "^1.2.5", - "pkg-conf": "^3.1.0", - "xdg-basedir": "^4.0.0" - }, - "engines": { - "node": ">=8.10" - } - }, - "node_modules/standard/node_modules/@eslint/eslintrc": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", - "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "lodash": "^4.17.20", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/standard/node_modules/eslint": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz", - "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.3.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.2.0", - "esutils": "^2.0.2", - "file-entry-cache": "^6.0.0", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash": "^4.17.20", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.4", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/standard/node_modules/eslint-plugin-import": { - "version": "2.24.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz", - "integrity": "sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.3", - "array.prototype.flat": "^1.2.4", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.6.2", - "find-up": "^2.0.0", - "has": "^1.0.3", - "is-core-module": "^2.6.0", - "minimatch": "^3.0.4", - "object.values": "^1.1.4", - "pkg-up": "^2.0.0", - "read-pkg-up": "^3.0.0", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.11.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0" - } - }, - "node_modules/standard/node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/standard/node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/standard/node_modules/eslint-plugin-promise": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.1.tgz", - "integrity": "sha512-XgdcdyNzHfmlQyweOPTxmc7pIsS6dE4MvwhXWMQ2Dxs1XAL2GJDilUsjWen6TWik0aSI+zD/PqocZBblcm9rdA==", - "dev": true, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "peerDependencies": { - "eslint": "^7.0.0" - } - }, - "node_modules/standard/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/standard/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/standard/node_modules/globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "dependencies": { - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/standard/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/standard/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/standard/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/standard/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/standard/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/standard/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/standard/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stream-chain": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz", - "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==" - }, - "node_modules/stream-json": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.8.0.tgz", - "integrity": "sha512-HZfXngYHUAr1exT4fxlbc1IOce1RYxp2ldeaf97LYCOPSoOqY/1Psp7iGvpb+6JIOgkra9zDYnPX01hGAHzEPw==", - "dependencies": { - "stream-chain": "^2.2.5" - } - }, - "node_modules/stream-shift": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", - "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==" - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", - "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", - "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom-buffer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/strip-bom-buffer/-/strip-bom-buffer-0.1.1.tgz", - "integrity": "sha512-dbIOX/cOLFgLH/2ofd7n78uPD3uPkXyt3P1IgaVoGiPYEdOnb7D1mawyhOTXyYWva1kCuRxJY5FkMsVKYlZRRg==", - "dependencies": { - "is-buffer": "^1.1.0", - "is-utf8": "^0.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-bom-string": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-0.1.2.tgz", - "integrity": "sha512-3DgNqQFTfOwWgxn3cXsa6h/WRgFa7dVb6/7YqwfJlBpLSSQbiU1VhaBNRKmtLI59CHjc9awLp9yGJREu7AnaMQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/success-symbol": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/success-symbol/-/success-symbol-0.1.0.tgz", - "integrity": "sha512-7S6uOTxPklNGxOSbDIg4KlVLBQw1UiGVyfCUYgYxrZUKRblUkmGj7r8xlfQoFudvqLv6Ap5gd76/IIFfI9JG2A==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/table": { - "version": "6.8.2", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", - "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", - "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", - "dev": true - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/tdigest": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", - "integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==", - "dependencies": { - "bintrees": "1.0.2" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/thread-stream": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-0.15.2.tgz", - "integrity": "sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==", - "dependencies": { - "real-require": "^0.1.0" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" - }, - "node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-object-path/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toad-cache": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", - "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true - }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typeof-article": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/typeof-article/-/typeof-article-0.1.1.tgz", - "integrity": "sha512-Vn42zdX3FhmUrzEmitX3iYyLb+Umwpmv8fkZRIknYh84lmdrwqZA5xYaoKiIj2Rc5i/5wcDrpUmZcbk1U51vTw==", - "dependencies": { - "kind-of": "^3.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/typeof-article/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", - "dev": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/v8-compile-cache": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", - "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", - "dev": true - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/varint": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", - "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==" - }, - "node_modules/vinyl": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", - "integrity": "sha512-Ci3wnR2uuSAWFMSglZuB8Z2apBdtOyz8CV7dC6/U1XbltXBC+IuutUkXQISz01P+US2ouBuesSbV6zILZ6BuzQ==", - "dependencies": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", - "replace-ext": "0.0.1" - }, - "engines": { - "node": ">= 0.9" - } - }, - "node_modules/vlq": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", - "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==" - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" - }, - "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/xxhash-wasm": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz", - "integrity": "sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==" - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index fcf6262b..00000000 --- a/package.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "name": "qryn", - "version": "3.1.1", - "description": "Polyglot Observability Stack with ClickHouse storage", - "main": "qryn_node.js", - "bin": { - "qryn": "./qryn_node.js" - }, - "scripts": { - "test": "jest --maxWorkers 1 --forceExit", - "start": "node qryn.mjs", - "pretty": "node qryn.mjs | pino-pretty", - "postinstall": "patch-package", - "install-view": "mkdir -p view && curl -L https://github.com/metrico/cloki-view/releases/latest/download/dist.zip | busybox unzip - -d ./view", - "lint": "npx eslint --fix *.js lib parser plugins test", - "bun-install": "bun install --platform node", - "bun-start": "bun run --bun qryn.mjs" - }, - "standard": { - "env": [ - "jest" - ] - }, - "repository": { - "type": "git", - "url": "git+ssh://git@github.com/metrico/qryn.git" - }, - "author": "lorenzo.mangani@gmail.com", - "license": "AGPL-3.0", - "bugs": { - "url": "https://github.com/metrico/qryn/issues" - }, - "homepage": "https://github.com/metrico/qryn#readme", - "dependencies": { - "@apla/clickhouse": "^1.6.4", - "@cloki/clickhouse-sql": "1.2.10", - "@fastify/compress": "^6.5.0", - "@fastify/cors": "^8.4.1", - "@fastify/formbody": "^7.4.0", - "@fastify/static": "^6.12.0", - "@fastify/websocket": "^8.2.0", - "@fastify/url-data": "^5.4.0", - "@fastify/basic-auth": "^5.1.0", - "@qxip/influx-line-protocol-parser": "^0.2.1", - "@qxip/plugnplay": "^3.3.1", - "@stricjs/router": "^5.0.6", - "axios": "^1.6.8", - "bnf": "^1.0.1", - "csv-writer": "^1.6.0", - "date-fns": "^2.27.0", - "fast-querystring": "^1.1.0", - "fastify": "^4.24.3", - "fastify-metrics": "^10.3.3", - "fastify-plugin": "^4.5.1", - "glob": "^7.1.2", - "handlebars": "^4.7.7", - "handlebars-utils": "^1.0.6", - "is-number": "^7.0.0", - "http-errors": "^2.0.0", - "json-stable-stringify": "^1.0.1", - "jsonic": "^1.0.1", - "logfmt": "^1.3.2", - "node-gzip": "^1.1.2", - "patch-package": "^6.4.7", - "pino": "^7.6.5", - "plugnplay": "npm:@qxip/plugnplay@^3.3.1", - "protobufjs": "7.2.6", - "protocol-buffers": "^4.2.0", - "protocol-buffers-encodings": "^1.1.1", - "record-cache": "^1.1.1", - "scramjet": "^4.36.1", - "short-hash": "^1.0.0", - "snappyjs": "^0.6.1", - "stream-chain": "^2.2.4", - "stream-json": "^1.7.3", - "ws": "^8.17.1", - "xxhash-wasm": "^0.4.2", - "yaml": "^1.10.2", - "@stricjs/utils": "^1.6.1", - "basic-auth": "^2.0.1", - "google-protobuf": "^3.21.2", - "@grpc/grpc-js": "^1.10.6", - "@grpc/proto-loader": "^0.7.12", - "pako": "^2.1.0" - }, - "devDependencies": { - "@elastic/elasticsearch": "=8.6.0", - "@influxdata/influxdb-client": "^1.33.2", - "@opentelemetry/api": "^1.0.2", - "@opentelemetry/exporter-trace-otlp-proto": "^0.50.0", - "@opentelemetry/instrumentation": "^0.25.0", - "@opentelemetry/instrumentation-connect": "^0.30.0", - "@opentelemetry/instrumentation-http": "^0.25.0", - "@opentelemetry/resources": "^0.25.0", - "@opentelemetry/sdk-trace-base": "^0.25.0", - "@opentelemetry/sdk-trace-node": "^0.25.0", - "@opentelemetry/semantic-conventions": "^0.25.0", - "casual": "^1.6.2", - "eslint": "^7.32.0", - "eslint-config-standard": "^16.0.3", - "eslint-plugin-import": "^2.25.3", - "eslint-plugin-jest": "^25.3.0", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^5.2.0", - "jest": "^29.7.0", - "node-abort-controller": "^3.1.1", - "node-fetch": "^2.6.7", - "pino-pretty": "^7.5.1", - "prometheus-remote-write": "^0.3.0", - "standard": "^16.0.4", - "protobufjs": ">=7.2.4" - }, - "directories": { - "lib": "lib" - }, - "keywords": [ - "logs", - "opentelemetry", - "logql", - "promql", - "grafana", - "clickhouse", - "clickhouse-server", - "tempoql", - "tempo", - "tail", - "prometheus" - ] -} diff --git a/parser/bnf.js b/parser/bnf.js deleted file mode 100644 index 7b76fde1..00000000 --- a/parser/bnf.js +++ /dev/null @@ -1,122 +0,0 @@ -const { getPlg } = require('../plugins/engine') -const registryNames = [ - 'high_level_aggregation_registry', - 'log_range_aggregation_registry', - 'number_operator_registry', - 'stream_selector_operator_registry', - 'line_filter_operator_registry', - 'parser_registry', - 'unwrap_registry', - 'parameterized_aggregation_registry', - 'parameterized_unwrapped_registry' -] -const path = require('path') -const registries = registryNames.reduce((sum, n) => { - sum[n] = require(path.join(__dirname, 'registry', n)) - return sum -}, {}) -const fs = require('fs') - -const { Compiler } = require('bnf/Compiler') -const { Token } = require('bnf/Token') - -Token.prototype.Children = function (tokenType) { - let tokens = [] - for (let i = 0; i < this.tokens.length; i++) { - if (this.tokens[i].name === tokenType) { - tokens.push(this.tokens[i]) - } - tokens = [...tokens, ...this.tokens[i].Children(tokenType)] - } - - return tokens -} - -Token.prototype.dropAll = function (tokenType) { - this.tokens = this.tokens.filter(t => t.name !== tokenType) - this.tokens.forEach(t => t.dropAll(tokenType)) - return this -} - -let bnf = fs.readFileSync(path.join(__dirname, 'logql.bnf')).toString() -for (const reg of Object.keys(registries)) { - const keys = Object.keys(registries[reg]).map(n => `"${n}"`) - keys.sort((a, b) => b.length - a.length) - bnf = bnf.replace(`<${reg}>`, keys.join('|')) -} -const plugins = getPlg({ type: 'macros' }) -bnf += Object.values(plugins).map(p => p.bnf).join('\n') + '\n' -bnf += 'user_macro ::=' + Object.values(plugins).map(p => p._main_rule_name).map(n => `<${n}>`).join('|') + '\n' - -const compiler = new Compiler() -compiler.AddLanguage(bnf, 'logql') - -const BNF_CORE_RULES = new Set([ - 'BLANK', 'CR', 'LF', 'CRLF', 'DIGIT', 'DIGITS', - 'NUMBER', 'WSP', 'TAB', 'SPACE', 'OWSP', 'ANYWSP', 'ALPHA', 'SYMBOL', 'ESCAPE', - 'QUOTE', 'SQUOTE', 'AQUOTE', 'ANYCHAR', 'SQLITERAL', 'QLITERAL', 'AQLITERAL', - 'LITERAL', 'ANYLITERAL', 'EOF' -]) - -for (const [name, rule] of Object.entries(compiler.languages.logql.rules)) { - for (const token of rule) { - if (token.type === 1 && !compiler.languages.logql.rules[token.value] && !BNF_CORE_RULES.has(token.value)) { - const re = new RegExp(`^\\s*${name}\\s*::=`) - const line = compiler.languages.logql._syntaxLines.find( - l => l.original.match(re) - ).original - throw new Error(`BNF error in line "${line}": \n Rule "${token.value}": not found`) - } - } -} - -compiler._ParseScript = compiler.ParseScript -/** - * hack to avoid ridiculously long strings - * @param script {string} - * @constructor - */ -compiler.ParseScript = function (script) { - const qLiterals = [] - const aqLiterals = [] - let _script = script - let res = '' - const re = /^([^"`]*)("(([^"\\]|\\.)*)"|`(([^`\\]|\\.)*)`)?/ - let qsMatch = _script.match(re) - while (qsMatch && qsMatch[0]) { - let repl = qsMatch[2] || qsMatch[4] || '' - if (repl.length > 512) { - if (repl.startsWith('"')) { - qLiterals.push(repl) - repl = `"QL_${qLiterals.length - 1}"` - } else { - aqLiterals.push(repl) - repl = `\`AL_${aqLiterals.length - 1}\`` - } - } - res = res + qsMatch[1] + repl - _script = _script.slice(qsMatch[0].length) - qsMatch = _script.match(re) - } - const parsedScript = this._ParseScript(res) - if (!parsedScript) { - return parsedScript - } - for (const t of parsedScript.rootToken.Children('QLITERAL')) { - if (!t.value.match(/^"QL_\d+"$/)) { - continue - } - t._value = qLiterals[parseInt(t.value.slice(4, t.value.length - 1))] - t.tokens = [] - } - for (const t of parsedScript.rootToken.Children('AQLITERAL')) { - if (!t.value.match(/^`AL_\d+`$/)) { - continue - } - t._value = aqLiterals[parseInt(t.value.slice(4, t.value.length - 1))] - t.tokens = [] - } - return parsedScript -} - -module.exports = compiler diff --git a/parser/logql.bnf b/parser/logql.bnf deleted file mode 100644 index 645fa152..00000000 --- a/parser/logql.bnf +++ /dev/null @@ -1,79 +0,0 @@ -<SYNTAX> ::= <log_stream_selector> | <agg_statement> | <summary> | <user_macro> - -log_stream_fp_selector ::= "{" <OWSP> <log_stream_selector_rule> *(<OWSP> "," <OWSP> <log_stream_selector_rule>) <OWSP> "}" -log_stream_selector ::= <log_stream_fp_selector> <OWSP> *(<OWSP><log_pipeline>) - -log_stream_selector_rule ::= <label> <OWSP> <operator> <OWSP> <quoted_str> -label ::= (<ALPHA> | "_") *(<ALPHA> | "." | "_" | <DIGITS>) -operator ::= <stream_selector_operator_registry> -quoted_str ::= (<QUOTE><QUOTE>) | (<AQUOTE><AQUOTE>) | <QLITERAL> | <AQLITERAL> - -log_pipeline ::= <line_filter_expression> | <parser_expression> | <label_filter_pipeline> | <line_format_expression> | <labels_format_expression> - -line_filter_expression ::= <line_filter_operator> <OWSP> <quoted_str> -line_filter_operator ::= <line_filter_operator_registry> - -parser_expression ::= "|" <OWSP> <parser_fn_name> <OWSP> <opt_parameters> -parser_fn_name ::= <parser_registry> -opt_parameters ::= <parameter> <OWSP> *("," <OWSP> <parameter>) | <BLANK> -parameter ::= <label> <OWSP> "=" <OWSP> <quoted_str> | <quoted_str> | <label> - -label_filter_pipeline ::= "|" <OWSP> <complex_label_filter_expression> -complex_label_filter_expression ::= (<label_filter_expression> | <bracketed_label_filter_expression>) *(<OWSP> <and_or> <WSP> <OWSP> <label_filter_expression> | <OWSP> <and_or> <WSP> <OWSP> <bracketed_label_filter_expression>) -bracketed_label_filter_expression ::= "(" <OWSP> <complex_label_filter_expression> <OWSP> ")" -and_or ::= "and" | "or" -label_filter_expression ::= <string_label_filter_expression> | <number_label_filter_expression> - -string_label_filter_expression ::= <label> <OWSP> <operator> <OWSP> <quoted_str> - -number_label_filter_expression ::= <label> <OWSP> <number_operator> <OWSP> <number_value> -number_operator ::= <number_operator_registry> -number_value ::= <duration_value> | <bytes_value> | <NUMBER> -duration_value ::= <NUMBER> ("ns"|"us"|"ms"|"s"|"m"|"h"|"d"|"w") -bytes_value ::= <NUMBER> ("b"|"kib"|"kb"|"mb"|"mib"|"gib"|"gb"|"lib"|"tb"|"pib"|"pb"|"eib"|"eb") - -line_format_fn ::= "line_format_native" | "line_format" -line_format_expression ::= "|" <OWSP> <line_format_fn> <OWSP> <quoted_str> - - -labels_format_expression ::= "|" <OWSP> "label_format" <OWSP> <labels_format_expression_param> *(<OWSP> "," <OWSP> <labels_format_expression_param>) -labels_format_expression_param ::= <label_rename_param> | <label_inject_param> -label_rename_param ::= <label> <OWSP> "=" <OWSP> <label> -label_inject_param ::= <label> <OWSP> "=" <OWSP> <quoted_str> - -log_range_aggregation ::= <log_range_aggregation_fn> <OWSP> "(" <OWSP> <log_stream_selector> <OWSP> "[" <duration_value> "]" <OWSP> ")" -log_range_aggregation_fn ::= <log_range_aggregation_registry> - -aggregation_operator ::= <aggregation_operator_fn> <OWSP> (<prefix_agg_operator> | <postfix_agg_operator>) - -postfix_agg_operator ::= "(" <OWSP> [<NUMBER> <OWSP> "," <OWSP>] (<log_range_aggregation> | <unwrap_function>) <OWSP> ")" [<OWSP> <req_by_without> <OWSP>] - -prefix_agg_operator ::= <req_by_without> <OWSP> "(" <OWSP> [<NUMBER> <OWSP> "," <OWSP>] (<log_range_aggregation> | <unwrap_function>) <OWSP> ")" - -aggregation_operator_fn ::= <high_level_aggregation_registry> -opt_by_without ::= <req_by_without> | <BLANK> -req_by_without ::= <by_without> <OWSP> "(" <OWSP> <label_list> <OWSP> ")" -by_without ::= "by" | "without" -label_list ::= <label> <OWSP> *("," <OWSP> <label>) - -unwrap_expression ::= <log_stream_selector> <OWSP> <unwrap_statement> -unwrap_statement ::= "|" <OWSP> ("unwrap" 1*<WSP> <label> | <unwrap_value_statement>) -unwrap_function ::= <unwrap_fn> <OWSP> "(" <OWSP> [<scalar_param> <OWSP> ","] <OWSP> <unwrap_expression> <OWSP> "[" <duration_value> "]" <OWSP> ")" [ <OWSP> <req_by_without_unwrap> ] -scalar_param ::= <NUMBER> -unwrap_fn ::= <unwrap_registry> -req_by_without_unwrap ::= <by_without_unwrap> <OWSP> "(" <OWSP> <label_list> <OWSP> ")" -by_without_unwrap ::= "by" | "without" - -agg_statement ::= (<aggregation_operator> | <log_range_aggregation> | <unwrap_function> | <parameterized_expression> | <parameterized_unwrapped_expression>) [<OWSP> <compared_agg_statement_cmp>] -compared_agg_statement_cmp ::= <number_operator> <OWSP> <number_value> - -unwrap_value_statement ::= "unwrap_value" - -parameterized_unwrapped_expression ::= <parameterized_unwrapped_expression_fn><OWSP>"("<OWSP><parameter_value><OWSP>","<OWSP><unwrap_expression><OWSP>"["<duration_value>"]"<OWSP>")" [ <OWSP> <req_by_without_unwrap> ] [<OWSP> <compared_agg_statement_cmp>] -parameterized_unwrapped_expression_fn ::= <parameterized_unwrapped_registry> - -parameterized_expression ::= <parameterized_expression_fn><OWSP>"("<OWSP><parameter_value><OWSP>","<OWSP>(<agg_statement>|<parameterized_unwrapped_expression>)<OWSP>")" [<OWSP> <compared_agg_statement_cmp>] -parameter_value ::= <NUMBER> -parameterized_expression_fn ::= <parameterized_aggregation_registry> - -summary ::= "summary" <OWSP> "(" <OWSP> <log_stream_selector> <OWSP> ")" diff --git a/parser/registry/common.js b/parser/registry/common.js deleted file mode 100644 index 8857a99d..00000000 --- a/parser/registry/common.js +++ /dev/null @@ -1,463 +0,0 @@ -const { hashLabels, parseLabels } = require('../../common') -const { getPlg } = require('../../plugins/engine') -const Sql = require('@cloki/clickhouse-sql') -const { DATABASE_NAME } = require('../../lib/utils') -const clusterName = require('../../common').clusterName -module.exports.dist = clusterName ? '_dist' : '' - -/** - * @param query {registry_types.Request | string[]} - * @param clauses {string[]} - * @returns {registry_types.Request | string[]} - */ -module.exports._and = (query, clauses) => { - if (Array.isArray(query)) { - if (!query.length) { - return ['AND', ...clauses] - } - return query[0] === 'AND' - ? [...query, ...clauses] - : ['AND', query, ...clauses] - } - query = { ...query } - if (!query.where) { - query.where = ['AND'] - } else if (query.where[0] !== 'AND') { - query.where = ['AND', query.where] - } else { - query.where = [...query.where] - } - query.where.push.apply(query.where, clauses) - return query -} - -/** - * - * @param query {Select} - * @returns {DataStream[]} - */ -module.exports.getStream = (query) => { - return query && query.ctx && query.ctx.stream ? query.ctx.stream : [] -} - -/** - * - * @param query {Select} - * @returns {boolean} - */ -module.exports.hasStream = (query) => { - return module.exports.getStream(query).length > 0 -} - -/** - * - * @param query {Select} - * @param stream {function(DataStream): DataStream} - * @returns {Select} - */ -module.exports.addStream = (query, stream) => { - if (!query) { - throw new Error('query is undefined') - } - if (query && query.ctx && query.ctx.stream) { - query.ctx.stream.push(stream) - return query - } - if (query && query.ctx) { - query.ctx.stream = [stream] - return query - } - query.ctx = { stream: [stream] } - return query -} - -/** - * @param query {registry_types.Request} - * @returns {registry_types.Request} - */ -module.exports.querySelectorPostProcess = (query) => { - return query -} - -/** - * - * @param token {Token} - * @returns {string} - */ -module.exports.unquoteToken = (token) => { - const value = token.Child('quoted_str').value - if (value.startsWith('"')) { - return JSON.parse(value) - } - return value.substr(1, value.length - 2) -} - -/** - * - * @param s {DataStream} - * @param fn - * @returns {DataStream} - */ -module.exports.map = (s, fn) => s.map((e) => { - return new Promise((resolve) => { - setImmediate(() => { - resolve(fn(e)) - }) - }) -}) - -/** - * - * @param token {Token} - * @returns {number} - */ -module.exports.getDuration = (token) => { - return module.exports.durationToMs(token.Child('duration_value').value) - // Math.max(duration, query.ctx && query.ctx.step ? query.ctx.step : 1000); -} - -const getDuration = module.exports.getDuration - -/** - * - * @param eof {any} - * @returns boolean - */ -module.exports.isEOF = (eof) => eof && eof.EOF - -/** - * - * @param type {string} - * @param cb {(function(any): any) | undefined} - * @returns {Object<string, (function(any): any)>} - */ -module.exports.getPlugins = (type, cb) => { - const _plgs = getPlg({ type: type }) - const plgs = {} - for (const _e of Object.values(_plgs)) { - for (const e of Object.entries(_e)) { - plgs[e[0]] = cb ? cb(e[1]) : () => e[1] - } - } - return plgs - /* for (let file of glob.sync(path + "/*.js")) { - const mod = require(file); - for (let fn of Object.keys(mod)) { - plugins[fn] = cb ? cb(mod[fn]()) : mod[fn](); - } - } - return plugins; */ -} - -/** - * - * @param query {Select} - * @returns {boolean} - */ -module.exports.hasExtraLabels = (query) => { - return query.select().some(f => f[1] === 'extra_labels') -} - -/** - * - * @param query {Select} - * @returns {SQLObject} - */ -module.exports.concatLabels = (query) => { - if (module.exports.hasExtraLabels(query)) { - return new Sql.Raw('arraySort(arrayConcat(arrayFilter(x -> arrayExists(y -> y.1 == x.1, extra_labels) == 0, labels), extra_labels))') - } - return new Sql.Raw('labels') -} - -/** - * sum_over_time(unwrapped-range): the sum of all values in the specified interval. - * @param token {Token} - * @param query {Select} - * @param byWithoutName {string} name of the by_without token - * @returns {Select} - */ -function applyByWithoutStream (token, query, byWithoutName) { - const isBy = token.Child(byWithoutName).value === 'by' - const filterLabels = token.Children('label').map(l => l.value) - return module.exports.addStream(query, - /** - * - * @param stream {DataStream} - */ - (stream) => stream.map(e => { - if (!e || !e.labels) { - return e - } - const labels = [...Object.entries(e.labels)].filter(l => - (isBy && filterLabels.includes(l[0])) || (!isBy && !filterLabels.includes(l[0])) - ) - return { ...e, labels: parseLabels(labels) } - })) -} - -/** - * - * @param values {Object} - * @param timestamp {number} - * @param value {number} - * @param duration {number} - * @param step {number} - * @param counterFn {function(any, any, number): any} - * @returns {Object} - */ -function addTimestamp (values, timestamp, value, duration, step, counterFn) { - const timestampWithoutStep = Math.floor(timestamp / duration) * duration - const timestampWithStep = step > duration - ? Math.floor(timestampWithoutStep / step) * step - : timestampWithoutStep - if (!values) { - values = {} - } - if (!values[timestampWithStep]) { - values[timestampWithStep] = {} - } - if (!values[timestampWithStep][timestampWithoutStep]) { - values[timestampWithStep][timestampWithoutStep] = 0 - } - values[timestampWithStep][timestampWithoutStep] = - counterFn(values[timestampWithStep][timestampWithoutStep], value, timestamp) - return values -} - -/** - * - * @param query {Select} - * @returns {boolean} - */ -module.exports.hasExtraLabels = (query) => { - return query.select().some((x) => x[1] === 'extra_labels') -} - -module.exports.timeShiftViaStream = (token, query) => { - let tsMoveParam = null - if (!query.params.timestamp_shift) { - tsMoveParam = new Sql.Parameter('timestamp_shift') - query.addParam(tsMoveParam) - } else { - tsMoveParam = query.params.timestamp_shift - } - const duration = module.exports.getDuration(token) - /** - * @param s {DataStream} - */ - const stream = (s) => s.map((e) => { - if (tsMoveParam.get()) { - e.timestamp_ns -= (parseInt(tsMoveParam.get()) % duration) - } - return e - }) - return module.exports.addStream(query, stream) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @param counterFn {function(any, any, number): any} - * @param summarizeFn {function(any): number} - * @param lastValue {boolean} if the applier should take the latest value in step (if step > duration) - * @param byWithoutName {string} name of the by_without token - * @returns {Select} - */ -module.exports.applyViaStream = (token, query, - counterFn, summarizeFn, lastValue, byWithoutName) => { - query.ctx.matrix = true - byWithoutName = byWithoutName || 'by_without' - if (token.Child(byWithoutName)) { - query = applyByWithoutStream(token.Child(`req_${byWithoutName}`), query, byWithoutName) - } - let results = new Map() - const duration = getDuration(token, query) - query.ctx.duration = duration - const step = query.ctx.step - /** - * @param s {DataStream} - */ - const stream = (s) => s.remap((emit, e) => { - if (!e || !e.labels) { - for (const v of results.values()) { - const ts = [...Object.entries(v.values)] - ts.sort() - for (const _v of ts) { - let value = Object.entries(_v[1]) - value.sort() - value = lastValue ? value[value.length - 1][1] : value[0][1] - value = summarizeFn(value)// Object.values(_v[1]).reduce((sum, v) => sum + summarizeFn(v), 0); - emit({ labels: v.labels, timestamp_ns: _v[0], value: value }) - } - } - results = new Map() - emit({ EOF: true }) - return - } - const l = hashLabels(e.labels) - if (!results.has(l)) { - results.set(l, { - labels: e.labels, - values: addTimestamp(undefined, e.timestamp_ns, e, duration, step, counterFn) - }) - } else { - results.get(l).values = addTimestamp( - results.get(l).values, e.timestamp_ns, e, duration, step, counterFn - ) - } - }) - return module.exports.addStream(query, stream) -} - -/** - * - * @param str {string} - * @param custom {(function(string): string | undefined) | undefined} - * @param customSlash {(function(string): (string | undefined)) | undefined} - * @return {string} - */ -module.exports.unquote = (str, custom, customSlash) => { - const quote = str.substr(0, 1) - switch (quote) { - case '"': - case '`': - break - default: - throw new Error(`Unknown quote: ${quote}`) - } - str = str.trim() - str = str.substr(1, str.length - 2) - let res = '' - let slash = false - for (let i = 0; i < str.length; i++) { - if (!slash) { - if (custom && custom(str[i])) { - res += custom(str[i]) - continue - } - if (str[i] === quote) { - throw new Error('Unexpected quote') - } - switch (str[i]) { - case '\\': - slash = true - continue - default: - res += str[i] - } - } - if (slash) { - slash = false - if (customSlash && customSlash(str[i])) { - res += customSlash(str[i]) - continue - } - if (str[i] === quote) { - res += quote - continue - } - switch (str[i]) { - case 'r': - res += '\r' - break - case 'n': - res += '\n' - break - case 't': - res += '\t' - break - default: - res += '\\' + str[i] - } - } - } - return res -} - -module.exports.sharedParamNames = { - samplesTable: 'samplesTable', - timeSeriesTable: 'timeSeriesTable', - from: 'from', - to: 'to', - limit: 'limit' -} - -/** - * - * @param durationStr {string} - * @returns {number} - */ -module.exports.durationToMs = require('../../common').durationToMs - -module.exports.Aliased = class { - constructor (name, alias) { - this.name = name - this.alias = alias - } - - toString () { - return `${Sql.quoteTerm(this.name)} AS ${this.alias}` - } -} - -/** - * @param query {Select} - * @param name {string} - * @param patcher {function(Object): Object} - */ -module.exports.patchCol = (query, name, patcher) => { - query.select_list = query.select().map(col => { - const _name = Array.isArray(col) ? col[1] : col - if (_name === name) { - return patcher(col[0]) - } - return col - }) -} - -module.exports.preJoinLabels = (token, query, dist) => { - const from = query.getParam(module.exports.sharedParamNames.from) - const to = query.getParam(module.exports.sharedParamNames.to) - const sqlFrom = new Sql.Raw() - sqlFrom.toString = () => { - let fromNs = 0 - if (from.get()) { - fromNs = from.get() - } - return `toDate(fromUnixTimestamp(intDiv(${fromNs}, 1000000000)))` - } - const sqlTo = new Sql.Raw() - sqlTo.toString = () => { - let toNs = 0 - if (to.get()) { - toNs = to.get() - } - return `toDate(fromUnixTimestamp(intDiv(${toNs}, 1000000000)))` - } - let withIdxSel = query.with().idx_sel - let inRightSide = new Sql.WithReference(withIdxSel) - if (!withIdxSel) { - withIdxSel = query.with().str_sel - inRightSide = new Sql.Select() - .select('fingerprint') - .from(new Sql.WithReference(withIdxSel)) - } - dist = dist || '' - const timeSeriesReq = new Sql.Select() - .select('fingerprint', 'labels') - .from([`${DATABASE_NAME()}.time_series`, 'time_series']) - .where(new Sql.And( - new Sql.In('time_series.fingerprint', 'in', inRightSide), - Sql.Gte(new Sql.Raw('date'), sqlFrom), - Sql.Lte(new Sql.Raw('date'), sqlTo) - )) - timeSeriesReq._toString = timeSeriesReq.toString - timeSeriesReq.toString = () => { - return `(${timeSeriesReq._toString()})` - } - query.join(new module.exports.Aliased(timeSeriesReq, 'time_series'), 'left any', - Sql.Eq('samples.fingerprint', new Sql.Raw('time_series.fingerprint'))) - query.select([new Sql.Raw('JSONExtractKeysAndValues(time_series.labels, \'String\')'), 'labels']) -} diff --git a/parser/registry/complex_label_filter_expression.js b/parser/registry/complex_label_filter_expression.js deleted file mode 100644 index 5ae666c9..00000000 --- a/parser/registry/complex_label_filter_expression.js +++ /dev/null @@ -1,223 +0,0 @@ -const reg = require('./stream_selector_operator_registry/stream_selector_operator_registry') -const numreg = require('./number_operator_registry/compared_label_reg') -const { hasExtraLabels, hasStream, addStream } = require('./common') -const Sql = require('@cloki/clickhouse-sql') - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports = (token, query) => { - if (hasStream(query)) { - const pred = processStreamExpression(token, query) - return addStream(query, - /** - * - * @param e {DataStream} - * @returns {DataStream} - */ - (e) => e.filter(pred)) - } - const ex = processWhereExpression(token, query) - return hasExtraLabels(query) - ? query.where(ex) - : reg.simpleAnd(query, ex) -} - -/** - * - * @param andOr {string} - * @param cond {Conditions} - */ -const checkAndOrType = (andOr, cond) => { - return (andOr === 'and' && cond instanceof Sql.Conjunction) || - (andOr === 'or' && cond instanceof Sql.Disjunction) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Conditions} - */ -const processWhereExpression = (token, query) => { - let where = null - let andOr = null - for (const t of token.tokens) { - if (t.name === 'and_or') { - andOr = t.value - continue - } - andOr = (andOr || 'and').toLowerCase() - let ex = null - if (t.name === 'label_filter_expression') { - ex = getLabelFilterWhereExpression(t, query) - } else if (t.name === 'bracketed_label_filter_expression' || t.name === 'complex_label_filter_expression') { - ex = processWhereExpression(t, query) - } else { - continue - } - if (!where) { - where = ex - } else if (checkAndOrType(andOr, where)) { - where.args.push(ex) - } else if (andOr === 'and') { - where = Sql.And(where, ex) - } else if (andOr === 'or') { - where = Sql.Or(where, ex) - } - andOr = null - } - return where -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {string | string[]} - */ -const getLabelFilterWhereExpression = (token, query) => { - // TODO: - let clauses = null - if (token.Child('string_label_filter_expression')) { - switch (token.Child('operator').value) { - case '=': - clauses = hasExtraLabels(query) ? reg.eqExtraLabels(token) : reg.eqSimple(token) - break - case '!=': - clauses = hasExtraLabels(query) ? reg.neqExtraLabels(token) : reg.neqSimple(token) - break - case '=~': - clauses = hasExtraLabels(query) ? reg.regExtraLabels(token) : reg.regSimple(token) - break - case '!~': - clauses = hasExtraLabels(query) ? reg.nregExtraLabels(token) : reg.nregSimple(token) - break - default: - throw new Error('Unsupported operator') - } - return clauses - } - if (token.Child('number_label_filter_expression')) { - const label = token.Child('label').value - if (token.Child('duration_value') || token.Child('bytes_value')) { - throw new Error('Not supported') - } - const val = token.Child('number_value').value - const idx = hasExtraLabels(query) ? 'extraLabelsWhere' : 'simpleWhere' - switch (token.Child('number_operator').value) { - case '==': - return numreg[idx].eq(label, val) - case '!=': - return numreg[idx].neq(label, val) - case '>': - return numreg[idx].gt(label, val) - case '>=': - return numreg[idx].ge(label, val) - case '<': - return numreg[idx].lt(label, val) - case '<=': - return numreg[idx].le(label, val) - } - } -} - -/** - * - * @param fns {(function({labels: Object}): boolean)} - * @returns {function({labels: Object}): boolean} - */ -const genericAnd = (...fns) => { - return (e) => !fns.some(fn => !fn(e)) -} - -/** - * - * @param fns {(function({labels: Object}): boolean)} - * @returns {function({labels: Object}): boolean} - */ -const genericOr = (...fns) => { - return (e) => fns.some(fn => fn(e)) -} - -/** - * - * @param token {Token} - * @param query {registry_types.Request} - * @returns {function({labels: Object}): boolean} - */ -const processStreamExpression = (token, query) => { - let andOr = 'and' - let res = null - for (const t of token.tokens) { - if (t.name === 'label_filter_expression') { - if (!res) { - res = getLabelFilterStreamExpression(t, query) - continue - } - res = (andOr || 'and').toLowerCase() === 'and' - ? genericAnd(res, getLabelFilterStreamExpression(t, query)) - : genericOr(res, getLabelFilterStreamExpression(t, query)) - } - if (t.name === 'bracketed_label_filter_expression' || t.name === 'complex_label_filter_expression') { - if (!res) { - res = processStreamExpression(t, query) - continue - } - res = (andOr || 'and').toLowerCase() === 'and' - ? genericAnd(res, processStreamExpression(t, query)) - : genericOr(res, processStreamExpression(t, query)) - } - if (t.name === 'and_or') { - andOr = t.value - } - } - return res || (() => true) -} - -/** - * - * @param token {Token} - * @param query {registry_types.Request} - * @returns {function({labels: Object}): boolean} - */ -const getLabelFilterStreamExpression = (token, query) => { - if (token.Child('string_label_filter_expression')) { - switch (token.Child('operator').value) { - case '=': - return reg.eqStream(token, query) - case '!=': - return reg.neqStream(token, query) - case '=~': - return reg.regStream(token, query) - case '!~': - return reg.nregStream(token, query) - default: - throw new Error('Unsupported operator') - } - } - if (token.Child('number_label_filter_expression')) { - const label = token.Child('label').value - if (token.Child('duration_value') || token.Child('bytes_value')) { - throw new Error('Not supported') - } - const val = token.Child('number_value').value - switch (token.Child('number_operator').value) { - case '==': - return numreg.streamWhere.eq(label, val) - case '!=': - return numreg.streamWhere.neq(label, val) - case '>': - return numreg.streamWhere.gt(label, val) - case '>=': - return numreg.streamWhere.ge(label, val) - case '<': - return numreg.streamWhere.lt(label, val) - case '<=': - return numreg.streamWhere.le(label, val) - } - } -} diff --git a/parser/registry/high_level_aggregation_registry/high_level_agg_reg.js b/parser/registry/high_level_aggregation_registry/high_level_agg_reg.js deleted file mode 100644 index 3c107555..00000000 --- a/parser/registry/high_level_aggregation_registry/high_level_agg_reg.js +++ /dev/null @@ -1,133 +0,0 @@ -const { applyViaStream, hasStream } = require('../common') -const Sql = require('@cloki/clickhouse-sql') -/** - * - * @param token {Token} - * @returns [string, string[]] - */ -function getByWithout (token) { - return token.Child('by_without') - ? [ - token.Child('by_without').value.toString().toLowerCase(), - token.Child('req_by_without').Children('label').map(c => c.value) - ] - : [undefined, undefined] -} - -/** - * - * @param expression {string} - * @param stream {(function(Token, Select): Select)} - * @returns {(function(Token, Select): Select)} - */ -module.exports.genericRequest = (expression, stream) => { - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - return (token, query) => { - if (hasStream(query)) { - return stream(token, query) - } - const [byWithout, labelList] = getByWithout(token) - if (!byWithout) { - return query - } - const labelsFilterClause = new Sql.Raw(`arrayFilter(x -> x.1 ${byWithout === 'by' ? 'IN' : 'NOT IN'} ` + - `(${labelList.map(l => `'${l}'`).join(',')}), labels)`) - query.ctx.matrix = true - const aggA = new Sql.With('agg_a', query) - return (new Sql.Select()) - .with(aggA) - .select( - [labelsFilterClause, 'labels'], - 'timestamp_ns', - [new Sql.Raw(expression), 'value']) - .from(new Sql.WithReference(aggA)) - .groupBy('labels', 'timestamp_ns') - .orderBy('labels', 'timestamp_ns') - } -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.streamSum = (token, query) => { - return applyViaStream(token, query, (sum, e) => { - sum = sum || 0 - return sum + e.value - }, (sum) => sum, false) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.streamMin = (token, query) => { - return applyViaStream(token, query, (sum, e) => { - return sum ? Math.min(sum.value, e.value) : { value: e.value } - }, sum => sum.value) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.streamMax = (token, query) => { - return applyViaStream(token, query, (sum, e) => { - return sum ? Math.max(sum.value, e.value) : { value: e.value } - }, sum => sum.value) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.streamAvg = (token, query) => { - return applyViaStream(token, query, (sum, e) => { - return sum ? { value: sum.value + e.value, count: sum.count + 1 } : { value: e.value, count: 1 } - }, sum => sum.value / sum.count) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.streamStddev = (token, query) => { - throw new Error('Not implemented') -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.streamStdvar = (token, query) => { - throw new Error('Not implemented') -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.streamCount = (token, query) => { - return applyViaStream(token, query, (sum) => { - return sum ? sum + 1 : 1 - }, sum => sum) -} diff --git a/parser/registry/high_level_aggregation_registry/index.js b/parser/registry/high_level_aggregation_registry/index.js deleted file mode 100644 index b2557360..00000000 --- a/parser/registry/high_level_aggregation_registry/index.js +++ /dev/null @@ -1,18 +0,0 @@ -const reg = require('./high_level_agg_reg') -const { genericRequest } = reg - -module.exports = { - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - sum: genericRequest('sum(value)', reg.streamSum), - min: genericRequest('min(value)', reg.streamMin), - max: genericRequest('max(value)', reg.streamMax), - avg: genericRequest('avg(value)', reg.streamAvg), - stddev: genericRequest('stddevPop(value)', reg.streamStddev), - stdvar: genericRequest('varPop(value)', reg.streamStdvar), - count: genericRequest('count(1)', reg.streamCount) -} diff --git a/parser/registry/line_filter_operator_registry.js b/parser/registry/line_filter_operator_registry.js deleted file mode 100644 index 3b14bbcc..00000000 --- a/parser/registry/line_filter_operator_registry.js +++ /dev/null @@ -1,69 +0,0 @@ -const { unquoteToken } = require('./common') -const Sql = require('@cloki/clickhouse-sql') - -/** - * @param val {string} - * @returns {string} - */ -const likePercent = (val) => { - if (!val) { - return "'%'" - } - val = Sql.quoteVal(val).toString() - val = val.substring(1, val.length - 1) - val = val.replace(/([%_])/g, '\\$1') - return `'%${val}%'` -} - -module.exports = { - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - '|=': (token, query) => { - const val = unquoteToken(token) - if (!val) { - return query - } - query.where(Sql.Ne(new Sql.Raw(`like(string, ${likePercent(val)})`), 0)) - return query - }, - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - '|~': (token, query) => { - const val = unquoteToken(token) - if (!val) { - return query - } - query.where(Sql.Eq(new Sql.Raw(`match(string, ${Sql.quoteVal(val)})`), new Sql.Raw('1'))) - return query - }, - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - '!=': (token, query) => { - const val = unquoteToken(token) - query.where(Sql.Eq(new Sql.Raw(`notLike(string, ${likePercent(val)})`), 1)) - return query - }, - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - '!~': (token, query) => { - const val = unquoteToken(token) - query.where(Sql.Eq(new Sql.Raw(`match(string, ${Sql.quoteVal(val)})`), new Sql.Raw('0'))) - return query - } -} diff --git a/parser/registry/line_format/go_native_fmt.js b/parser/registry/line_format/go_native_fmt.js deleted file mode 100644 index 557ba69d..00000000 --- a/parser/registry/line_format/go_native_fmt.js +++ /dev/null @@ -1,33 +0,0 @@ -const fs = require('fs') -const path = require('path') -let inst = null - -module.exports.compile = async (format) => { - if (!inst) { - require('./wasm_exec') - const go = new global.Go() - const wasm = fs.readFileSync(path.join(__dirname, 'go_txttmpl.wasm')) - const wasmModule = await WebAssembly.instantiate(wasm, go.importObject) - go.run(wasmModule.instance) - inst = true - } - const res = global.GO_TXTTMPL_NewTemplate(format) - if (res.err) { - throw new Error(res.err) - } - return { - process: (labels) => { - return global.GO_TXTTMPL_ProcessLine(res.id, Object.entries(labels).map(e => `${e[0]}\x01${e[1]}`).join('\x01')) - }, - done: () => { - global.GO_TXTTMPL_ReleaseTemplate(res.id) - } - } -} - -module.exports.stop = () => { - if (inst) { - global.GO_TXTTMPL_End() - inst = null - } -} diff --git a/parser/registry/line_format/go_txttmpl.wasm b/parser/registry/line_format/go_txttmpl.wasm deleted file mode 100755 index 27e746de..00000000 Binary files a/parser/registry/line_format/go_txttmpl.wasm and /dev/null differ diff --git a/parser/registry/line_format/index.js b/parser/registry/line_format/index.js deleted file mode 100644 index 271c18f4..00000000 --- a/parser/registry/line_format/index.js +++ /dev/null @@ -1,66 +0,0 @@ -const hb = require('handlebars') -const { addStream, isEOF } = require('../common') -const { LineFmtOption } = require('../../../common') -const { compile } = require('./go_native_fmt') -const logger = require('../../../lib/logger') -require('../../../lib/handlebars-helpers')(['math', 'string'], { - handlebars: hb -}) - -/** - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports = (token, query) => { - let processor = null - const fmt = JSON.parse('"' + token.Child('quoted_str').value.replace(/(^"|^'|"$|'$)/g, '') + '"') - if (LineFmtOption() === 'go_native' || token.Child('line_format_fn').value === 'line_format_native') { - processor = compile(fmt) - processor.then((p) => { - processor = p - }) - } else { - processor = { - process: hb.compile(fmt), - done: () => {} - } - } - return addStream(query, - /** - * - * @param s {DataStream} - */ - (s) => s.map(async (e) => { - if (!e) { - return e - } - if (isEOF(e)) { - processor.done() - return e - } - if (!e.labels) { - return e - } - if (!processor) { - return null - } - if (processor.then) { - try { - await processor - } catch (err) { - processor = null - logger.error({ err }) - } - } - try { - const res = processor.process({ ...e.labels, _entry: e.string }) - return { - ...e, - string: res - } - } catch (err) { - return null - } - }).filter(e => e)) -} diff --git a/parser/registry/line_format/wasm_exec.js b/parser/registry/line_format/wasm_exec.js deleted file mode 100644 index ca9fc629..00000000 --- a/parser/registry/line_format/wasm_exec.js +++ /dev/null @@ -1,641 +0,0 @@ - -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* eslint-disable */ - -(() => { - // Map multiple JavaScript environments to a single common API, - // preferring web standards over Node.js API. - // - // Environments considered: - // - Browsers - // - Node.js - // - Electron - // - Parcel - // - Webpack - - if (typeof global !== "undefined") { - // global already exists - } else if (typeof window !== "undefined") { - window.global = window; - } else if (typeof self !== "undefined") { - self.global = self; - } else { - throw new Error("cannot export Go (neither global, window nor self is defined)"); - } - - if (!global.require && typeof require !== "undefined") { - global.require = require; - } - - if (!global.fs && global.require) { - const fs = require("fs"); - if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) { - global.fs = fs; - } - } - - const enosys = () => { - const err = new Error("not implemented"); - err.code = "ENOSYS"; - return err; - }; - - if (!global.fs) { - let outputBuf = ""; - global.fs = { - constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused - writeSync(fd, buf) { - outputBuf += decoder.decode(buf); - const nl = outputBuf.lastIndexOf("\n"); - if (nl != -1) { - console.log(outputBuf.substr(0, nl)); - outputBuf = outputBuf.substr(nl + 1); - } - return buf.length; - }, - write(fd, buf, offset, length, position, callback) { - if (offset !== 0 || length !== buf.length || position !== null) { - callback(enosys()); - return; - } - const n = this.writeSync(fd, buf); - callback(null, n); - }, - chmod(path, mode, callback) { callback(enosys()); }, - chown(path, uid, gid, callback) { callback(enosys()); }, - close(fd, callback) { callback(enosys()); }, - fchmod(fd, mode, callback) { callback(enosys()); }, - fchown(fd, uid, gid, callback) { callback(enosys()); }, - fstat(fd, callback) { callback(enosys()); }, - fsync(fd, callback) { callback(null); }, - ftruncate(fd, length, callback) { callback(enosys()); }, - lchown(path, uid, gid, callback) { callback(enosys()); }, - link(path, link, callback) { callback(enosys()); }, - lstat(path, callback) { callback(enosys()); }, - mkdir(path, perm, callback) { callback(enosys()); }, - open(path, flags, mode, callback) { callback(enosys()); }, - read(fd, buffer, offset, length, position, callback) { callback(enosys()); }, - readdir(path, callback) { callback(enosys()); }, - readlink(path, callback) { callback(enosys()); }, - rename(from, to, callback) { callback(enosys()); }, - rmdir(path, callback) { callback(enosys()); }, - stat(path, callback) { callback(enosys()); }, - symlink(path, link, callback) { callback(enosys()); }, - truncate(path, length, callback) { callback(enosys()); }, - unlink(path, callback) { callback(enosys()); }, - utimes(path, atime, mtime, callback) { callback(enosys()); }, - }; - } - - if (!global.process) { - global.process = { - getuid() { return -1; }, - getgid() { return -1; }, - geteuid() { return -1; }, - getegid() { return -1; }, - getgroups() { throw enosys(); }, - pid: -1, - ppid: -1, - umask() { throw enosys(); }, - cwd() { throw enosys(); }, - chdir() { throw enosys(); }, - } - } - - if (!global.crypto && global.require) { - const nodeCrypto = require("crypto"); - global.crypto = { - getRandomValues(b) { - nodeCrypto.randomFillSync(b); - }, - }; - } - if (!global.crypto) { - throw new Error("global.crypto is not available, polyfill required (getRandomValues only)"); - } - - if (!global.performance) { - global.performance = { - now() { - const [sec, nsec] = process.hrtime(); - return sec * 1000 + nsec / 1000000; - }, - }; - } - - if (!global.TextEncoder && global.require) { - global.TextEncoder = require("util").TextEncoder; - } - if (!global.TextEncoder) { - throw new Error("global.TextEncoder is not available, polyfill required"); - } - - if (!global.TextDecoder && global.require) { - global.TextDecoder = require("util").TextDecoder; - } - if (!global.TextDecoder) { - throw new Error("global.TextDecoder is not available, polyfill required"); - } - - // End of polyfills for common API. - - const encoder = new TextEncoder("utf-8"); - const decoder = new TextDecoder("utf-8"); - - global.Go = class { - constructor() { - this.argv = ["js"]; - this.env = {}; - this.exit = (code) => { - if (code !== 0) { - console.warn("exit code:", code); - } - }; - this._exitPromise = new Promise((resolve) => { - this._resolveExitPromise = resolve; - }); - this._pendingEvent = null; - this._scheduledTimeouts = new Map(); - this._nextCallbackTimeoutID = 1; - - const setInt64 = (addr, v) => { - this.mem.setUint32(addr + 0, v, true); - this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true); - } - - const getInt64 = (addr) => { - const low = this.mem.getUint32(addr + 0, true); - const high = this.mem.getInt32(addr + 4, true); - return low + high * 4294967296; - } - - const loadValue = (addr) => { - const f = this.mem.getFloat64(addr, true); - if (f === 0) { - return undefined; - } - if (!isNaN(f)) { - return f; - } - - const id = this.mem.getUint32(addr, true); - return this._values[id]; - } - - const storeValue = (addr, v) => { - const nanHead = 0x7FF80000; - - if (typeof v === "number" && v !== 0) { - if (isNaN(v)) { - this.mem.setUint32(addr + 4, nanHead, true); - this.mem.setUint32(addr, 0, true); - return; - } - this.mem.setFloat64(addr, v, true); - return; - } - - if (v === undefined) { - this.mem.setFloat64(addr, 0, true); - return; - } - - let id = this._ids.get(v); - if (id === undefined) { - id = this._idPool.pop(); - if (id === undefined) { - id = this._values.length; - } - this._values[id] = v; - this._goRefCounts[id] = 0; - this._ids.set(v, id); - } - this._goRefCounts[id]++; - let typeFlag = 0; - switch (typeof v) { - case "object": - if (v !== null) { - typeFlag = 1; - } - break; - case "string": - typeFlag = 2; - break; - case "symbol": - typeFlag = 3; - break; - case "function": - typeFlag = 4; - break; - } - this.mem.setUint32(addr + 4, nanHead | typeFlag, true); - this.mem.setUint32(addr, id, true); - } - - const loadSlice = (addr) => { - const array = getInt64(addr + 0); - const len = getInt64(addr + 8); - return new Uint8Array(this._inst.exports.mem.buffer, array, len); - } - - const loadSliceOfValues = (addr) => { - const array = getInt64(addr + 0); - const len = getInt64(addr + 8); - const a = new Array(len); - for (let i = 0; i < len; i++) { - a[i] = loadValue(array + i * 8); - } - return a; - } - - const loadString = (addr) => { - const saddr = getInt64(addr + 0); - const len = getInt64(addr + 8); - return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); - } - - const timeOrigin = Date.now() - performance.now(); - this.importObject = { - go: { - // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) - // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported - // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). - // This changes the SP, thus we have to update the SP used by the imported function. - - // func wasmExit(code int32) - "runtime.wasmExit": (sp) => { - sp >>>= 0; - const code = this.mem.getInt32(sp + 8, true); - this.exited = true; - delete this._inst; - delete this._values; - delete this._goRefCounts; - delete this._ids; - delete this._idPool; - this.exit(code); - }, - - // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) - "runtime.wasmWrite": (sp) => { - sp >>>= 0; - const fd = getInt64(sp + 8); - const p = getInt64(sp + 16); - const n = this.mem.getInt32(sp + 24, true); - fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); - }, - - // func resetMemoryDataView() - "runtime.resetMemoryDataView": (sp) => { - sp >>>= 0; - this.mem = new DataView(this._inst.exports.mem.buffer); - }, - - // func nanotime1() int64 - "runtime.nanotime1": (sp) => { - sp >>>= 0; - setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); - }, - - // func walltime() (sec int64, nsec int32) - "runtime.walltime": (sp) => { - sp >>>= 0; - const msec = (new Date).getTime(); - setInt64(sp + 8, msec / 1000); - this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true); - }, - - // func scheduleTimeoutEvent(delay int64) int32 - "runtime.scheduleTimeoutEvent": (sp) => { - sp >>>= 0; - const id = this._nextCallbackTimeoutID; - this._nextCallbackTimeoutID++; - this._scheduledTimeouts.set(id, setTimeout( - () => { - this._resume(); - while (this._scheduledTimeouts.has(id)) { - // for some reason Go failed to register the timeout event, log and try again - // (temporary workaround for https://github.com/golang/go/issues/28975) - console.warn("scheduleTimeoutEvent: missed timeout event"); - this._resume(); - } - }, - getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early - )); - this.mem.setInt32(sp + 16, id, true); - }, - - // func clearTimeoutEvent(id int32) - "runtime.clearTimeoutEvent": (sp) => { - sp >>>= 0; - const id = this.mem.getInt32(sp + 8, true); - clearTimeout(this._scheduledTimeouts.get(id)); - this._scheduledTimeouts.delete(id); - }, - - // func getRandomData(r []byte) - "runtime.getRandomData": (sp) => { - sp >>>= 0; - crypto.getRandomValues(loadSlice(sp + 8)); - }, - - // func finalizeRef(v ref) - "syscall/js.finalizeRef": (sp) => { - sp >>>= 0; - const id = this.mem.getUint32(sp + 8, true); - this._goRefCounts[id]--; - if (this._goRefCounts[id] === 0) { - const v = this._values[id]; - this._values[id] = null; - this._ids.delete(v); - this._idPool.push(id); - } - }, - - // func stringVal(value string) ref - "syscall/js.stringVal": (sp) => { - sp >>>= 0; - storeValue(sp + 24, loadString(sp + 8)); - }, - - // func valueGet(v ref, p string) ref - "syscall/js.valueGet": (sp) => { - sp >>>= 0; - const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 32, result); - }, - - // func valueSet(v ref, p string, x ref) - "syscall/js.valueSet": (sp) => { - sp >>>= 0; - Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); - }, - - // func valueDelete(v ref, p string) - "syscall/js.valueDelete": (sp) => { - sp >>>= 0; - Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16)); - }, - - // func valueIndex(v ref, i int) ref - "syscall/js.valueIndex": (sp) => { - sp >>>= 0; - storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); - }, - - // valueSetIndex(v ref, i int, x ref) - "syscall/js.valueSetIndex": (sp) => { - sp >>>= 0; - Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); - }, - - // func valueCall(v ref, m string, args []ref) (ref, bool) - "syscall/js.valueCall": (sp) => { - sp >>>= 0; - try { - const v = loadValue(sp + 8); - const m = Reflect.get(v, loadString(sp + 16)); - const args = loadSliceOfValues(sp + 32); - const result = Reflect.apply(m, v, args); - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 56, result); - this.mem.setUint8(sp + 64, 1); - } catch (err) { - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 56, err); - this.mem.setUint8(sp + 64, 0); - } - }, - - // func valueInvoke(v ref, args []ref) (ref, bool) - "syscall/js.valueInvoke": (sp) => { - sp >>>= 0; - try { - const v = loadValue(sp + 8); - const args = loadSliceOfValues(sp + 16); - const result = Reflect.apply(v, undefined, args); - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 40, result); - this.mem.setUint8(sp + 48, 1); - } catch (err) { - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 40, err); - this.mem.setUint8(sp + 48, 0); - } - }, - - // func valueNew(v ref, args []ref) (ref, bool) - "syscall/js.valueNew": (sp) => { - sp >>>= 0; - try { - const v = loadValue(sp + 8); - const args = loadSliceOfValues(sp + 16); - const result = Reflect.construct(v, args); - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 40, result); - this.mem.setUint8(sp + 48, 1); - } catch (err) { - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 40, err); - this.mem.setUint8(sp + 48, 0); - } - }, - - // func valueLength(v ref) int - "syscall/js.valueLength": (sp) => { - sp >>>= 0; - setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); - }, - - // valuePrepareString(v ref) (ref, int) - "syscall/js.valuePrepareString": (sp) => { - sp >>>= 0; - const str = encoder.encode(String(loadValue(sp + 8))); - storeValue(sp + 16, str); - setInt64(sp + 24, str.length); - }, - - // valueLoadString(v ref, b []byte) - "syscall/js.valueLoadString": (sp) => { - sp >>>= 0; - const str = loadValue(sp + 8); - loadSlice(sp + 16).set(str); - }, - - // func valueInstanceOf(v ref, t ref) bool - "syscall/js.valueInstanceOf": (sp) => { - sp >>>= 0; - this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0); - }, - - // func copyBytesToGo(dst []byte, src ref) (int, bool) - "syscall/js.copyBytesToGo": (sp) => { - sp >>>= 0; - const dst = loadSlice(sp + 8); - const src = loadValue(sp + 32); - if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) { - this.mem.setUint8(sp + 48, 0); - return; - } - const toCopy = src.subarray(0, dst.length); - dst.set(toCopy); - setInt64(sp + 40, toCopy.length); - this.mem.setUint8(sp + 48, 1); - }, - - // func copyBytesToJS(dst ref, src []byte) (int, bool) - "syscall/js.copyBytesToJS": (sp) => { - sp >>>= 0; - const dst = loadValue(sp + 8); - const src = loadSlice(sp + 16); - if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) { - this.mem.setUint8(sp + 48, 0); - return; - } - const toCopy = src.subarray(0, dst.length); - dst.set(toCopy); - setInt64(sp + 40, toCopy.length); - this.mem.setUint8(sp + 48, 1); - }, - - "debug": (value) => { - console.log(value); - }, - } - }; - } - - async run(instance) { - if (!(instance instanceof WebAssembly.Instance)) { - throw new Error("Go.run: WebAssembly.Instance expected"); - } - this._inst = instance; - this.mem = new DataView(this._inst.exports.mem.buffer); - this._values = [ // JS values that Go currently has references to, indexed by reference id - NaN, - 0, - null, - true, - false, - global, - this, - ]; - this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id - this._ids = new Map([ // mapping from JS values to reference ids - [0, 1], - [null, 2], - [true, 3], - [false, 4], - [global, 5], - [this, 6], - ]); - this._idPool = []; // unused ids that have been garbage collected - this.exited = false; // whether the Go program has exited - - // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. - let offset = 4096; - - const strPtr = (str) => { - const ptr = offset; - const bytes = encoder.encode(str + "\0"); - new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes); - offset += bytes.length; - if (offset % 8 !== 0) { - offset += 8 - (offset % 8); - } - return ptr; - }; - - const argc = this.argv.length; - - const argvPtrs = []; - this.argv.forEach((arg) => { - argvPtrs.push(strPtr(arg)); - }); - argvPtrs.push(0); - - const keys = Object.keys(this.env).sort(); - keys.forEach((key) => { - argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); - }); - argvPtrs.push(0); - - const argv = offset; - argvPtrs.forEach((ptr) => { - this.mem.setUint32(offset, ptr, true); - this.mem.setUint32(offset + 4, 0, true); - offset += 8; - }); - - // The linker guarantees global data starts from at least wasmMinDataAddr. - // Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr. - const wasmMinDataAddr = 4096 + 8192; - if (offset >= wasmMinDataAddr) { - throw new Error("total length of command line and environment variables exceeds limit"); - } - - this._inst.exports.run(argc, argv); - if (this.exited) { - this._resolveExitPromise(); - } - await this._exitPromise; - } - - _resume() { - if (this.exited) { - throw new Error("Go program has already exited"); - } - this._inst.exports.resume(); - if (this.exited) { - this._resolveExitPromise(); - } - } - - _makeFuncWrapper(id) { - const go = this; - return function () { - const event = { id: id, this: this, args: arguments }; - go._pendingEvent = event; - go._resume(); - return event.result; - }; - } - } - - if ( - typeof module !== "undefined" && - global.require && - global.require.main === module && - global.process && - global.process.versions && - !global.process.versions.electron - ) { - if (process.argv.length < 3) { - console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); - process.exit(1); - } - - const go = new Go(); - go.argv = process.argv.slice(2); - go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env); - go.exit = process.exit; - WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { - process.on("exit", (code) => { // Node.js exits if no event handler is pending - if (code === 0 && !go.exited) { - // deadlock, make Go print error and stack traces - go._pendingEvent = { id: 0 }; - go._resume(); - } - }); - return go.run(result.instance); - }).catch((err) => { - console.error(err); - process.exit(1); - }); - } -})(); - -/* eslint-enable */ diff --git a/parser/registry/log_range_aggregation_registry/index.js b/parser/registry/log_range_aggregation_registry/index.js deleted file mode 100644 index b2cd1c5e..00000000 --- a/parser/registry/log_range_aggregation_registry/index.js +++ /dev/null @@ -1,129 +0,0 @@ -const { getDuration, hasStream } = require('../common') -const reg = require('./log_range_agg_reg') -const { genericRate } = reg -const Sql = require('@cloki/clickhouse-sql') -const { addStream } = require('../common') -const JSONstringify = require('json-stable-stringify') - -module.exports = { - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - rate: (token, query) => { - if (hasStream(query)) { - return reg.rateStream(token, query) - } - const duration = getDuration(token) - return genericRate(new Sql.Raw(`toFloat64(count(1)) * 1000 / ${duration}`), token, query) - }, - - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - count_over_time: (token, query) => { - if (hasStream(query)) { - return reg.countOverTimeStream(token, query) - } - return genericRate(new Sql.Raw('toFloat64(count(1))'), token, query) - }, - - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - bytes_rate: (token, query) => { - if (hasStream(query)) { - return reg.bytesRateStream(token, query) - } - const duration = getDuration(token, query) - return genericRate(new Sql.Raw(`toFloat64(sum(length(string))) * 1000 / ${duration}`), token, query) - }, - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - bytes_over_time: (token, query) => { - if (hasStream(query)) { - return reg.bytesOverTimeStream(token, query) - } - return genericRate(new Sql.Raw('toFloat64(sum(length(string)))'), token, query) - }, - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - absent_over_time: (token, query) => { - if (hasStream(query)) { - throw new Error('Not implemented') - } - query.ctx.matrix = true - const duration = getDuration(token) - query.select_list = [] - query.select('labels', - [new Sql.Raw(`toUInt64(intDiv(timestamp_ns, ${duration}000000) * ${duration})`), 'timestamp_ns'], - [new Sql.Raw('toFloat64(0)'), 'value']) - query.limit(undefined, undefined) - query.groupBy('labels', 'timestamp_ns') - query.orderBy(['labels', 'asc'], ['timestamp_ns', 'asc']) - query.ctx.matrix = true - let nextTS = query.ctx.start - let lastLabels = null - return addStream(query, (s) => s.remap((emit, val) => { - if (val.EOF && lastLabels) { - const lbls = JSON.parse(lastLabels) - for (let i = parseInt(nextTS); i < parseInt(query.ctx.end); i += duration) { - emit({ labels: lbls, value: 1, timestamp_ns: i }) - } - emit(val) - return - } - if (!val.labels) { - emit(val) - return - } - if (JSONstringify(val.labels) !== lastLabels) { - if (lastLabels) { - const lbls = JSON.parse(lastLabels) - for (let i = parseInt(nextTS); i < parseInt(query.ctx.end); i += duration) { - emit({ labels: lbls, value: 1, timestamp_ns: i }) - } - } - nextTS = query.ctx.start - lastLabels = JSONstringify(val.labels) - } - for (let i = parseInt(nextTS); i < val.timestamp_ns; i += duration) { - emit({ ...val, value: 1, timestamp_ns: i }) - } - emit(val) - nextTS = parseInt(val.timestamp_ns) + duration - })) - - /* { - ctx: query.ctx, - with: { - rate_a: queryData, - rate_b: queryGaps, - rate_c: { requests: [{ select: ['*'], from: 'rate_a' }, { select: ['*'], from: 'rate_b' }] } - }, - select: ['labels', 'timestamp_ns', 'min(value) as value'], // other than the generic - from: 'rate_c', - group_by: ['labels', 'timestamp_ns'], - order_by: { - name: ['labels', 'timestamp_ns'], - order: 'asc' - } - } */ - } -} diff --git a/parser/registry/log_range_aggregation_registry/log_range_agg_reg.js b/parser/registry/log_range_aggregation_registry/log_range_agg_reg.js deleted file mode 100644 index 03c8d8fe..00000000 --- a/parser/registry/log_range_aggregation_registry/log_range_agg_reg.js +++ /dev/null @@ -1,135 +0,0 @@ -const { getDuration, concatLabels, timeShiftViaStream } = require('../common') -const _applyViaStream = require('../common').applyViaStream -const Sql = require('@cloki/clickhouse-sql') - -/** - * - * @param token {Token} - * @param query {Select} - * @param counterFn {function(any, any, number): any} - * @param summarizeFn {function(any): number} - * @param lastValue {boolean} if the applier should take the latest value in step (if step > duration) - * @param byWithoutName {string} name of the by_without token - */ -const applyViaStream = (token, query, counterFn, summarizeFn, lastValue, byWithoutName) => { - query.limit(undefined, undefined) - query.ctx.matrix = true - return _applyViaStream(token, timeShiftViaStream(token, query), counterFn, summarizeFn, lastValue, byWithoutName) -} - -/** - * - * @param valueExpr {SQLObject} - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -const genericRate = (valueExpr, token, query) => { - const duration = getDuration(token) - query.ctx.matrix = true - query.ctx.duration = duration - query.limit(undefined, undefined) - const step = query.ctx.step - const rateA = new Sql.With('rate_a', query) - const tsMoveParam = new Sql.Parameter('timestamp_shift') - query.addParam(tsMoveParam) - const tsGroupingExpr = new Sql.Raw('') - tsGroupingExpr.toString = () => { - if (!tsMoveParam.get()) { - return `intDiv(timestamp_ns, ${duration}) * ${duration}` - } - return `intDiv(timestamp_ns - ${tsMoveParam.toString()}, ${duration}) * ${duration} + ${tsMoveParam.toString()}` - } - const rateB = (new Sql.Select()) - .select( - [concatLabels(query), 'labels'], - [tsGroupingExpr, 'timestamp_ns'], - [valueExpr, 'value'] - ) - .from(new Sql.WithReference(rateA)) - .groupBy('labels', 'timestamp_ns') - .orderBy(['labels', 'asc'], ['timestamp_ns', 'asc']) - if (step <= duration) { - return rateB.with(rateA) - } - const rateC = (new Sql.Select()) - .select( - 'labels', - [new Sql.Raw(`intDiv(timestamp_ns, ${step}) * ${step}`), 'timestamp_ns'], - [new Sql.Raw('argMin(rate_b.value, rate_b.timestamp_ns)'), 'value'] - ) - .from('rate_b') - .groupBy('labels', 'timestamp_ns') - .orderBy(['labels', 'asc'], ['timestamp_ns', 'asc']) - return rateC.with(rateA, new Sql.With('rate_b', rateB)) -} - -module.exports.genericRate = genericRate - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.rateStream = (token, query) => { - const duration = getDuration(token, query) - return applyViaStream(token, query, (sum) => { - sum = sum || 0 - ++sum - return sum - }, (sum) => sum * 1000 / duration, false) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.countOverTimeStream = (token, query) => { - return applyViaStream(token, query, (sum) => { - sum = sum || 0 - ++sum - return sum - }, (sum) => sum, false) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.bytesRateStream = (token, query) => { - const duration = getDuration(token) - return applyViaStream(token, query, (sum, entry) => { - sum = sum || 0 - sum += entry.string.length - return sum - }, (sum) => sum * 1000 / duration, false) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.bytesOverTimeStream = (token, query) => { - return applyViaStream(token, query, (sum, entry) => { - sum = sum || 0 - sum += entry.string.length - return sum - }, (sum) => sum, false) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.bytesOverTimeStream = (token, query) => { - throw new Error('Not Implemented') -} diff --git a/parser/registry/number_operator_registry/compared_agg_reg.js b/parser/registry/number_operator_registry/compared_agg_reg.js deleted file mode 100644 index 3e87b8b2..00000000 --- a/parser/registry/number_operator_registry/compared_agg_reg.js +++ /dev/null @@ -1,116 +0,0 @@ -const { hasStream, addStream } = require('../common') -const Sql = require('@cloki/clickhouse-sql') -/** - * - * @param token {Token} - * @returns {number} - */ -function getVal (token) { - const valTok = token.Child('compared_agg_statement_cmp').Child('number_value') - if (valTok.Child('duration_value') || valTok.Child('bytes_value')) { - throw new Error('Not Implemented') - } - return parseFloat(valTok.value.toString()) -} - -/** - * - * @param query {Select} - * @param streamProc {(function({value: number}): boolean)} - * @param whereClause {Conditions | Condition} - * @returns {Select} - */ -function genericReq (query, streamProc, whereClause) { - if (hasStream(query)) { - return addStream(query, (s) => s.filter((e) => e.EOF || streamProc(e))) - } - if (query.aggregations.length) { - return query.having(whereClause) - } - return query.where(whereClause) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.eq = (token, query) => { - const val = getVal(token) - return genericReq(query, - (e) => Math.abs(e.value - val) < 0.0000000001, - Sql.Eq('value', val) - ) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.neq = (token, query) => { - const val = getVal(token) - return genericReq(query, - (e) => Math.abs(e.value - val) > 0.0000000001, - Sql.Ne('value', val) - ) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.gt = (token, query) => { - const val = getVal(token) - return genericReq(query, - (e) => - e.value > val, - Sql.Gt('value', val) - ) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.ge = (token, query) => { - const val = getVal(token) - return genericReq(query, - (e) => e.value >= val, - Sql.Gte('value', val) - ) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.lt = (token, query) => { - const val = getVal(token) - return genericReq(query, - (e) => e.value < val, - Sql.Lt('value', val) - ) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.le = (token, query) => { - const val = getVal(token) - return genericReq(query, - (e) => e.value <= val, - Sql.Lte('value', val) - ) -} diff --git a/parser/registry/number_operator_registry/compared_label_reg.js b/parser/registry/number_operator_registry/compared_label_reg.js deleted file mode 100644 index 56a8d087..00000000 --- a/parser/registry/number_operator_registry/compared_label_reg.js +++ /dev/null @@ -1,162 +0,0 @@ -const { hasExtraLabels, hasStream, addStream } = require('../common') -const Sql = require('@cloki/clickhouse-sql') -/** - * - * @param token {Token} - * @param query {Select} - * @param index {string} - * @returns {Select} - */ -const genericReq = (token, query, index) => { - if (token.Child('number_value').Child('duration_value') || - token.Child('number_value').Child('bytes_value')) { - throw new Error('Not implemented') - } - const label = token.Child('label').value - const val = parseInt(token.Child('number_value').value) - if (isNaN(val)) { - throw new Error(token.Child('number_value').value + 'is not a number') - } - if (hasStream(query)) { - return addStream(query, (s) => s.filter(module.exports.streamWhere[index](label, val))) - } - if (hasExtraLabels(query)) { - return query.where(module.exports.extraLabelsWhere[index](label, val)) - } - return query.where(module.exports.simpleWhere[index](label, val)) -} - -/** - * - * @param label {string} - * @param val {string} - * @param sign {Function} - * @returns {Conditions} - */ -const genericSimpleLabelSearch = - (label, val, sign) => Sql.And( - Sql.Eq(new Sql.Raw(`JSONHas(labels, '${label}')`), 1), - sign(new Sql.Raw(`toFloat64OrNull(JSONExtractString(labels, '${label}'))`), val) - ) - -/** - * - * @param lbl {string} - * @param val {string} - * @param sign {Function} - * @returns {Conditions} - */ -const genericExtraLabelSearch = - (lbl, val, sign) => Sql.Or( - Sql.Ne(new Sql.Raw( - `arrayExists(x -> x.1 == '${lbl}' AND (coalesce(` + - sign(new Sql.Raw('toFloat64OrNull(x.2)'), val).toString() + ', 0)), extra_labels)' - ), 0), - Sql.And( - Sql.Eq(new Sql.Raw(`arrayExists(x -> x.1 == '${lbl}', extra_labels)`), 0), - Sql.Eq(new Sql.Raw(`arrayExists(x -> x.1 == '${lbl}', labels)`), 1), - sign(new Sql.Raw(`toFloat64OrNull(arrayFirst(x -> x.1 == '${lbl}', labels).2)`), val) - ) - ) - -const genericStreamSearch = (label, fn) => - (e) => { - if (!e || e.EOF) { - return true - } - if (!e || !e.labels || !e.labels[label]) { - return false - } - const val = parseFloat(e.labels[label]) - if (isNaN(val)) { - return false - } - return fn(val) - } - -module.exports.simpleWhere = { - eq: (label, val) => genericSimpleLabelSearch(label, val, Sql.Eq), - neq: (label, val) => genericSimpleLabelSearch(label, val, Sql.Ne), - ge: (label, val) => genericSimpleLabelSearch(label, val, Sql.Gte), - gt: (label, val) => genericSimpleLabelSearch(label, val, Sql.Gt), - le: (label, val) => genericSimpleLabelSearch(label, val, Sql.Lte), - lt: (label, val) => genericSimpleLabelSearch(label, val, Sql.Lt) -} - -module.exports.extraLabelsWhere = { - eq: (label, val) => genericExtraLabelSearch(label, val, Sql.Eq), - neq: (label, val) => genericExtraLabelSearch(label, val, Sql.Ne), - ge: (label, val) => genericExtraLabelSearch(label, val, Sql.Gte), - gt: (label, val) => genericExtraLabelSearch(label, val, Sql.Gt), - le: (label, val) => genericExtraLabelSearch(label, val, Sql.Lte), - lt: (label, val) => genericExtraLabelSearch(label, val, Sql.Lt) -} - -module.exports.streamWhere = { - eq: (label, val) => genericStreamSearch(label, (_val) => Math.abs(val - _val) < 1e-10), - neq: (label, val) => genericStreamSearch(label, (_val) => Math.abs(val - _val) > 1e-10), - ge: (label, val) => genericStreamSearch(label, (_val) => _val >= val), - gt: (label, val) => genericStreamSearch(label, (_val) => _val > val), - le: (label, val) => genericStreamSearch(label, (_val) => _val <= val), - lt: (label, val) => genericStreamSearch(label, (_val) => _val < val) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.eq = (token, query) => { - return genericReq(token, query, 'eq') -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.neq = (token, query) => { - return genericReq(token, query, 'neq') -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.gt = (token, query) => { - return genericReq(token, query, 'gt') -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.ge = (token, query) => { - return genericReq(token, query, 'ge') -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.lt = (token, query) => { - return genericReq(token, query, 'lt') -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.le = (token, query) => { - return genericReq(token, query, 'le') -} diff --git a/parser/registry/number_operator_registry/index.js b/parser/registry/number_operator_registry/index.js deleted file mode 100644 index ac435235..00000000 --- a/parser/registry/number_operator_registry/index.js +++ /dev/null @@ -1,84 +0,0 @@ -const aggReg = require('./compared_agg_reg') -const labelReg = require('./compared_label_reg') - -/** - * - * @param token {Token} - * @param query {Select} - * @param aggregatedProcessor {(function(Token, Select): Select)} - * @param labelComparer {(function(Token, Select): Select)} - * @returns {Select} - */ -function genericReq (token, query, - aggregatedProcessor, labelComparer) { - if ((token.name === 'agg_statement' || token.Child('agg_statement')) && - token.Child('compared_agg_statement_cmp')) { - return aggregatedProcessor(token, query) - } - if (token.name === 'number_label_filter_expression' || token.Child('number_label_filter_expression')) { - return labelComparer(token, query) - } - throw new Error('Not implemented') -} - -module.exports = { - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - '==': (token, query) => { - return genericReq(token, query, aggReg.eq, labelReg.eq) - }, - - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - '>': (token, query) => { - return genericReq(token, query, aggReg.gt, labelReg.gt) - }, - - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - '>=': (token, query) => { - return genericReq(token, query, aggReg.ge, labelReg.ge) - }, - - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - '<': (token, query) => { - return genericReq(token, query, aggReg.lt, labelReg.lt) - }, - - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - '<=': (token, query) => { - return genericReq(token, query, aggReg.le, labelReg.le) - }, - - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - '!=': (token, query) => { - return genericReq(token, query, aggReg.neq, labelReg.neq) - } -} diff --git a/parser/registry/parameterized_aggregation_registry/index.js b/parser/registry/parameterized_aggregation_registry/index.js deleted file mode 100644 index 53407f69..00000000 --- a/parser/registry/parameterized_aggregation_registry/index.js +++ /dev/null @@ -1,62 +0,0 @@ -const { hasStream } = require('../common') -const { QrynBadRequest } = require('../../../lib/handlers/errors') -const Sql = require('@cloki/clickhouse-sql') - -/** - * - * @param token {Token} - * @param query {Select} - * @param isTop {boolean} - * @returns {Select} - */ -const topBottom = (token, query, isTop) => { - if (hasStream(query)) { - throw new QrynBadRequest('Not supported') - } - - const parA = new Sql.With('par_a', query) - const len = parseInt(token.Child('parameter_value').value) - const lambda = isTop ? 'x -> (-x.1, x.2), ' : '' - const q1 = (new Sql.Select()) - .with(parA) - .select( - ['par_a.timestamp_ns', 'timestamp_ns'], - [new Sql.Raw( - `arraySlice(arraySort(${lambda}groupArray((par_a.value, par_a.labels))), 1, ${len})`), 'slice'] - ).from(new Sql.WithReference(parA)) - .groupBy('timestamp_ns') - - const parB = new Sql.With('par_b', q1) - return (new Sql.Select()) - .with(parB) - .select( - [new Sql.Raw('arr_b.1'), 'value'], - [new Sql.Raw('arr_b.2'), 'labels'], - ['par_b.timestamp_ns', 'timestamp_ns'] - ) - .from(new Sql.WithReference(parB)) - .join(['par_b.slice', 'arr_b'], 'array') - .orderBy('labels', 'timestamp_ns') -} - -module.exports = { - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - topk: (token, query) => { - return topBottom(token, query, true) - }, - - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - bottomk: (token, query) => { - return topBottom(token, query, false) - } -} diff --git a/parser/registry/parameterized_unwrapped_registry/index.js b/parser/registry/parameterized_unwrapped_registry/index.js deleted file mode 100644 index df49bd92..00000000 --- a/parser/registry/parameterized_unwrapped_registry/index.js +++ /dev/null @@ -1,47 +0,0 @@ -const { QrynBadRequest } = require('../../../lib/handlers/errors') -const { hasStream, getDuration } = require('../common') -const Sql = require('@cloki/clickhouse-sql') -const { applyByWithoutLabels } = require('../unwrap_registry/unwrap_registry') - -module.exports = { - /** - * quantileOverTime(scalar,unwrapped-range): the φ-quantile (0 ≤ φ ≤ 1) of the values in the specified interval. - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - quantile_over_time: (token, query) => { - if (hasStream(query)) { - throw new QrynBadRequest('Not supported') - } - query.ctx.matrix = true - const durationMS = getDuration(token) - query.ctx.duration = durationMS - const stepMS = query.ctx.step - const quantVal = parseFloat(token.Child('parameter_value').value) - const quantA = new Sql.With('quant_a', query) - const labels = applyByWithoutLabels(token.Child('req_by_without_unwrap'), query) - const quantB = (new Sql.Select()) - .with(quantA) - .select( - [labels, 'labels'], - [new Sql.Raw(`intDiv(quant_a.timestamp_ns, ${durationMS}) * ${durationMS}`), 'timestamp_ns'], - [new Sql.Raw(`quantile(${quantVal})(unwrapped)`), 'value'] - ).from(new Sql.WithReference(quantA)) - .groupBy('timestamp_ns', 'labels') - .orderBy('labels', 'timestamp_ns') - if (stepMS <= durationMS) { - return quantB - } - const withQuantB = new Sql.With('quant_b', quantB) - return (new Sql.Select()) - .with(withQuantB) - .select( - ['quant_b.labels', 'labels'], - [new Sql.Raw(`intDiv(quant_b.timestamp_ns, ${stepMS}) * ${stepMS}`), 'timestamp_ns'], - [new Sql.Raw('argMin(quant_b.value, quant_b.timestamp_ns)'), 'value']) - .from(new Sql.WithReference(withQuantB)) - .groupBy('labels', 'timestamp_ns') - .orderBy('labels', 'timestamp_ns') - } -} diff --git a/parser/registry/parser_registry/drop.js b/parser/registry/parser_registry/drop.js deleted file mode 100644 index f8a77909..00000000 --- a/parser/registry/parser_registry/drop.js +++ /dev/null @@ -1,47 +0,0 @@ -const { hasExtraLabels, patchCol, addStream } = require('../common') -const Sql = require('@cloki/clickhouse-sql') -/** - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -const viaClickhouseQuery = (token, query) => { - const labelsToDrop = token.Children('label').map(l => l.value) - const colsToPatch = ['labels'] - if (hasExtraLabels(query)) { - colsToPatch.push('extra_labels') - } - for (const colName of colsToPatch) { - patchCol(query, colName, (col) => { - const colVal = new Sql.Raw('') - colVal.toString = () => - `arrayFilter(x -> x.1 NOT IN (${labelsToDrop.map(Sql.quoteVal).join(',')}), ${col})` - return [colVal, colName] - }) - } - return query -} - -/** - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -const viaStream = (token, query) => { - const labelsToDrop = token.Children('label').map(l => l.value) - addStream(query, (s) => s.map(e => { - if (!e || !e.labels) { - return e - } - for (const l of labelsToDrop) { - delete e.labels[l] - } - return e - })) - return query -} - -module.exports = { - viaClickhouseQuery, - viaStream -} diff --git a/parser/registry/parser_registry/index.js b/parser/registry/parser_registry/index.js deleted file mode 100644 index b4529019..00000000 --- a/parser/registry/parser_registry/index.js +++ /dev/null @@ -1,89 +0,0 @@ -const json = require('./json') -const re = require('./regexp') -const { hasExtraLabels, getPlugins, isEOF, hasStream, addStream } = require('../common') -const logfmt = require('./logfmt') -const drop = require('./drop') -const logger = require('../../../lib/logger') - -module.exports = { - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - json: (token, query) => { - if (!token.Children('parameter').length || (query.stream && query.stream.length) || - hasExtraLabels(query)) { - return json.viaStream(token, query) - } - return json.viaClickhouseQuery(token, query) - }, - - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - logfmt: (token, query) => { - return logfmt.viaStream(token, query) - }, - - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - regexp: (token, query) => { - if (hasStream(query) || hasExtraLabels(query)) { - return re.viaStream(token, query) - } - try { - return re.viaRequest(token, query) - } catch (err) { - logger.error({ err }) - return re.viaStream(token, query) - } - }, - - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - drop: (token, query) => { - if (hasStream(query)) { - return drop.viaStream(token, query) - } - return drop.viaClickhouseQuery(token, query) - }, - - ...getPlugins('parser_registry', (plugin) => { - if (plugin.map) { - return (token, query) => { - const mapper = plugin.map(token.Children('parameter').map(p => p.value)) - return addStream(query, (s) => s.map((e) => { - if (!e || isEOF(e) || !e.labels || !e.string) { - return e - } - return mapper(e) - })) - } - } - if (plugin.remap) { - return (token, query) => { - const remapper = plugin.remap(token.Children('parameter').map(p => p.value)) - return addStream(query, (s) => s.remap((emit, e) => { - if (!e || isEOF(e) || !e.labels || !e.string) { - emit(e) - return - } - remapper(emit, e) - })) - } - } - }) -} diff --git a/parser/registry/parser_registry/json.js b/parser/registry/parser_registry/json.js deleted file mode 100644 index 0586ecbe..00000000 --- a/parser/registry/parser_registry/json.js +++ /dev/null @@ -1,159 +0,0 @@ -const { Compiler } = require('bnf/Compiler') -const { map, addStream } = require('../common') -const Sql = require('@cloki/clickhouse-sql') - -/** - * - * @type {function(Token): Object | undefined} - */ -const getLabels = (() => { - const compiler = new Compiler() - compiler.AddLanguage(` -<SYNTAX> ::= first_part *(part) -<first_part> ::= 1*(<ALPHA> | "_" | <DIGITS>) -<part> ::= ("." <first_part>) | "[" <QLITERAL> "]" | "[" <DIGITS> "]" - `, 'json_param') - /** - * @param token {Token} - * @returns {Object | undefined} - */ - return (token) => { - if (!token.Children('parameter').length) { - return undefined - } - return token.Children('parameter').reduce((sum, p) => { - const label = p.Child('label').value - let val = compiler.ParseScript(JSON.parse(p.Child('quoted_str').value)) - val = [ - val.rootToken.Child('first_part').value, - ...val.rootToken.Children('part').map(t => t.value) - ] - sum[label] = val - return sum - }, {}) - } -})() - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.viaClickhouseQuery = (token, query) => { - const labels = getLabels(token) - let exprs = Object.entries(labels).map(lbl => { - const path = lbl[1].map(path => { - if (path.startsWith('.')) { - return `'${path.substring(1)}'` - } - if (path.startsWith('["')) { - return `'${JSON.parse(path.substring(1, path.length - 1))}'` - } - if (path.startsWith('[')) { - return (parseInt(path.substring(1, path.length - 1)) + 1).toString() - } - return `'${path}'` - }) - const expr = `if(JSONType(samples.string, ${path.join(',')}) == 'String', ` + - `JSONExtractString(samples.string, ${path.join(',')}), ` + - `JSONExtractRaw(samples.string, ${path.join(',')}))` - return `('${lbl[0]}', ${expr})` - }) - exprs = new Sql.Raw("arrayFilter((x) -> x.2 != '', [" + exprs.join(',') + '])') - query.select_list = query.select_list.filter(f => f[1] !== 'extra_labels') - query.select([exprs, 'extra_labels']) - query.where(Sql.Eq(new Sql.Raw('isValidJSON(samples.string)'), 1)) - return query -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.viaStream = (token, query) => { - const labels = getLabels(token) - - /** - * - * @param {any} obj - * @param {string} prefix - * @returns {string|{}|null} - */ - const objToLabels = (obj, prefix) => { - if (Array.isArray(obj) || - obj === null - ) { - return null - } - if (typeof obj === 'object') { - let res = {} - for (const k of Object.keys(obj)) { - const label = prefix + (prefix ? '_' : '') + (k.replace(/[^a-zA-Z0-9_]/g, '_')) - const val = objToLabels(obj[k], label) - if (typeof val === 'object') { - res = { ...res, ...val } - continue - } - res[label] = val - } - return res - } - return obj.toString() - } - - /** - * - * @param {Object} obj - * @param {String[]} path - */ - const extractLabel = (obj, path) => { - let res = obj - for (const p of path) { - if (!res[p]) { - return undefined - } - res = res[p] - } - if (typeof res === 'object' || Array.isArray(res)) { - return JSON.stringify(res) - } - return res.toString() - } - - /** - * - * @param {Object} obj - * @param {Object} labels - */ - const extractLabels = (obj, labels) => { - const res = {} - for (const l of Object.keys(labels)) { - res[l] = extractLabel(obj, labels[l]) - } - return res - } - - /** - * - * @param {DataStream} stream - * @return {DataStream} - */ - const stream = (stream) => { - return map(stream, (e) => { - if (!e || !e.labels) { - return { ...e } - } - try { - const oString = JSON.parse(e.string) - const extraLabels = labels ? extractLabels(oString, labels) : objToLabels(oString, '') - return { ...e, labels: { ...e.labels, ...extraLabels } } - } catch (err) { - return undefined - } - }) - } - return addStream(query, stream) -} diff --git a/parser/registry/parser_registry/logfmt.js b/parser/registry/parser_registry/logfmt.js deleted file mode 100644 index f5ec1a58..00000000 --- a/parser/registry/parser_registry/logfmt.js +++ /dev/null @@ -1,41 +0,0 @@ -const { map, addStream } = require('../common') -const logfmt = require('logfmt') - -/** - * - * @param token {Token} - * @param query {registry_types.Request} - * @returns {registry_types.Request} - */ -module.exports.viaStream = (token, query) => { - /** - * - * @param {string} line - */ - - const extractLabels = (line) => { - const labels = logfmt.parse(line) - return Object.fromEntries(Object.entries(labels).filter(([k, v]) => typeof v === 'string')) - } - - /** - * - * @param {DataStream} stream - * @return {DataStream} - */ - const stream = (stream) => { - return map(stream, (e) => { - if (!e || !e.labels) { - return { ...e } - } - - try { - const extraLabels = extractLabels(e.string) - return { ...e, labels: { ...e.labels, ...extraLabels } } - } catch (err) { - return undefined - } - }) - } - return addStream(query, stream) -} diff --git a/parser/registry/parser_registry/regexp.js b/parser/registry/parser_registry/regexp.js deleted file mode 100644 index 35fc6af5..00000000 --- a/parser/registry/parser_registry/regexp.js +++ /dev/null @@ -1,134 +0,0 @@ -const { Compiler } = require('bnf/Compiler') -const { unquote, addStream } = require('../common') -const Sql = require('@cloki/clickhouse-sql') - -const reBnf = ` -<SYNTAX> ::= *(<literal> | <any_group>) -label ::= ( ALPHA | "_" ) *( ALPHA | DIGIT | "_" ) -literal ::= <quoted_brack> | <letter> -quoted_brack ::= "\\(" | "\\)" -letter = !"\\(" !"\\)" !"(" !")" %x0-ff -group_name ::= "?" "<" <label> ">" -group_tail ::= *( <literal> | <any_group>) -any_group ::= "(" [<group_name>] <group_tail> ")" -` - -const compiler = new Compiler() -compiler.AddLanguage(reBnf, 're') -/** - * - * @param token {Token} - * @param res {{val: string, name?: string}[]} - * @returns {{val: string, name?: string}[]} - */ -const walk = (token, res) => { - res = res || [] - if (token.name === 'any_group') { - if (token.tokens[1].name === 'group_name') { - res.push({ - name: token.tokens[1].Child('label').value, - val: token.tokens[2].value - }) - } else { - res.push({ - val: token.tokens.find(t => t.name === 'group_tail').value - }) - } - } - for (const t of token.tokens) { - res = walk(t, res) - } - return res -} - -/** - * - * @param token {Token} - * @returns {Token} - */ -const rmNames = (token) => { - if (token.tokens) { - token.tokens = token.tokens.filter(t => t.name !== 'group_name') - } - token.tokens.forEach(rmNames) - return token -} - -/** - * - * @param str {string} - * @returns {Token} - */ -const compile = (str) => { - const res = compiler.ParseScript(str, {}, 're') - if (res === null) { - throw new Error("can't compile") - } - return res.rootToken -} - -/** - * - * @param regexp {string} - * @returns {{labels: {val:string, name: string}[], re: string}} - */ -const extractRegexp = (regexp) => { - const re = compile(unquote(regexp, - null, - (s) => s === '\\' ? '\\\\' : undefined)) - const labels = walk(re, []) - const rmTok = rmNames(re) - return { - labels, - re: rmTok.value - } -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.viaRequest = (token, query) => { - const { labels, re } = extractRegexp(token.Child('parameter').value) - const namesArray = '[' + labels.map(l => `'${l.name}'` || '').join(',') + ']' - query.select_list = query.select_list.filter(f => f[1] !== 'extra_labels') - query.select([ - new Sql.Raw(`arrayFilter(x -> x.1 != '' AND x.2 != '', arrayZip(${namesArray}, ` + - `arrayMap(x -> x[length(x)], extractAllGroupsHorizontal(string, '${re}'))))`), - 'extra_labels']) - return query -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.viaStream = (token, query) => { - const re = new RegExp(unquote(token.Child('parameter').value)) - const getLabels = (m) => { - return m && m.groups ? m.groups : {} - } - addStream(query, (s) => s.map(e => { - return e.labels - ? { - ...e, - labels: { - ...e.labels, - ...getLabels(e.string.match(re)) - } - } - : e - })) - return query -} - -module.exports.internal = { - rmNames: rmNames, - walk: walk, - compile: compile, - extractRegexp: extractRegexp -} diff --git a/parser/registry/registry_types.d.ts b/parser/registry/registry_types.d.ts deleted file mode 100644 index effa9fc5..00000000 --- a/parser/registry/registry_types.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {DataStream} from "scramjet"; - -export namespace registry_types { - interface Request { - ctx?: {[k: string]: any}, - with?: {[k: string]: Request | UnionRequest} - select: (string | Object)[], - from: string, - left_join?: [{ - name: string, - on: (string | string[])[] - }], - where?: (string | string[])[], - limit?: number, - offset?: number, - order_by?: { - name: string[], - order: string - }, - group_by?: string[], - having?: (string | string[])[], - matrix?: boolean, - stream?: ((DataStream) => DataStream)[], - final: boolean, - distinct?: boolean - } - interface UnionRequest { - requests: Request[] - } -} \ No newline at end of file diff --git a/parser/registry/smart_optimizations/index.js b/parser/registry/smart_optimizations/index.js deleted file mode 100644 index 262c9d59..00000000 --- a/parser/registry/smart_optimizations/index.js +++ /dev/null @@ -1,18 +0,0 @@ -const optimizations = [require('./optimization_v3_2')] - -module.exports = { - /** - * - * @param token {Token} - * @param fromNS {number} - * @param toNS {number} - * @param stepNS {number} - */ - apply: (token, fromNS, toNS, stepNS) => { - const optimization = optimizations.find((opt) => opt.isApplicable(token, fromNS / 1000000)) - if (optimization) { - return optimization.apply(token, fromNS, toNS, stepNS) - } - return null - } -} diff --git a/parser/registry/smart_optimizations/log_range_agg_reg_v3_2.js b/parser/registry/smart_optimizations/log_range_agg_reg_v3_2.js deleted file mode 100644 index d5fe0ab4..00000000 --- a/parser/registry/smart_optimizations/log_range_agg_reg_v3_2.js +++ /dev/null @@ -1,69 +0,0 @@ -const { getDuration } = require('../common') -const Sql = require('@cloki/clickhouse-sql') -module.exports = { - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - rate: (token, query) => { - const duration = getDuration(token) - return genericRate(new Sql.Raw(`toFloat64(countMerge(count)) * 1000 / ${duration}`), token, query) - }, - - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - count_over_time: (token, query) => { - return genericRate(new Sql.Raw('toFloat64(countMerge(count))'), token, query) - }, - - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - bytes_rate: (token, query) => { - const duration = getDuration(token, query) - return genericRate(new Sql.Raw(`toFloat64(sum(bytes) * 1000 / ${duration})`), token, query) - }, - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - bytes_over_time: (token, query) => { - return genericRate(new Sql.Raw('toFloat64(sum(bytes))'), token, query) - } -} - -const genericRate = (valueExpr, token, query) => { - const duration = getDuration(token) - query.ctx.matrix = true - query.ctx.duration = duration - query.limit(undefined, undefined) - const tsGroupingExpr = new Sql.Raw(`intDiv(timestamp_ns, ${duration}000000) * ${duration}`) - query.select([tsGroupingExpr, 'timestamp_ns'], [valueExpr, 'value']) - .groupBy('fingerprint', 'timestamp_ns') - .orderBy(['fingerprint', 'asc'], ['timestamp_ns', 'asc']) - const step = query.ctx.step - if (step <= duration) { - return query - } - const rateC = (new Sql.Select()) - .select( - 'labels', - [new Sql.Raw(`intDiv(timestamp_ns, ${step}) * ${step}`), 'timestamp_ns'], - [new Sql.Raw('argMin(rate_b.value, rate_b.timestamp_ns)'), 'value'] - ) - .from('rate_b') - .groupBy('fingerprint', 'timestamp_ns') - .orderBy(['fingerprint', 'asc'], ['timestamp_ns', 'asc']) - return rateC.with(new Sql.With('rate_b', query)) -} diff --git a/parser/registry/smart_optimizations/optimization_v3_2.js b/parser/registry/smart_optimizations/optimization_v3_2.js deleted file mode 100644 index a74295fd..00000000 --- a/parser/registry/smart_optimizations/optimization_v3_2.js +++ /dev/null @@ -1,83 +0,0 @@ -const { getDuration, preJoinLabels, dist, sharedParamNames } = require('../common') -const reg = require('./log_range_agg_reg_v3_2') -const Sql = require('@cloki/clickhouse-sql') -const { DATABASE_NAME, checkVersion } = require('../../../lib/utils') -const streamSelectorReg = require('../stream_selector_operator_registry') -const aggOpReg = require('../high_level_aggregation_registry') -const { clusterName } = require('../../../common') -const logger = require('../../../lib/logger') -const _dist = clusterName ? '_dist' : '' - -/** - * - * @param token {Token} - * @param fromMS {number} - * @returns {boolean} - */ -module.exports.isApplicable = (token, fromMS) => { - let logAggFn = token.Child('log_range_aggregation_fn') - logAggFn = logAggFn ? logAggFn.value : null - if (!logAggFn) { - return false - } - const durationMs = getDuration(token) - return checkVersion('v3_2', fromMS) && - !isLogPipeline(token) && reg[logAggFn] && durationMs >= 15000 && durationMs % 15000 === 0 -} - -function isLogPipeline (token) { - let isPipeline = false - for (const pipeline of token.Children('log_pipeline')) { - isPipeline |= !pipeline.Child('line_filter_operator') || - !(pipeline.Child('line_filter_operator').value === '|=' && - ['""', '``'].includes(pipeline.Child('quoted_str').value)) - } - return isPipeline -} - -/** - * - * @param token {Token} - * @param fromNS {number} - * @param toNS {number} - * @param stepNS {number} - */ -module.exports.apply = (token, fromNS, toNS, stepNS) => { - fromNS = Math.floor(fromNS / 15000000000) * 15000000000 - const fromParam = new Sql.Parameter(sharedParamNames.from) - const toParam = new Sql.Parameter(sharedParamNames.to) - const tsClause = toNS - ? Sql.between('samples.timestamp_ns', fromNS, toNS) - : Sql.Gt('samples.timestamp_ns', fromNS) - let q = (new Sql.Select()) - .select(['samples.fingerprint', 'fingerprint']) - .from([`${DATABASE_NAME()}.metrics_15s${_dist}`, 'samples']) - .where(tsClause) - .addParam(fromParam) - .addParam(toParam) - fromParam.set(fromNS) - toParam.set(toNS) - - q.ctx = { - step: stepNS / 1000000000, - inline: !!clusterName - } - - for (const streamSelectorRule of token.Children('log_stream_selector_rule')) { - q = streamSelectorReg[streamSelectorRule.Child('operator').value](streamSelectorRule, q) - } - preJoinLabels(token, q, dist) - q = q.groupBy('labels') - - const lra = token.Child('log_range_aggregation') - q = reg[lra.Child('log_range_aggregation_fn').value](lra, q) - - const aggOp = token.Child('aggregation_operator') - if (aggOp) { - q = aggOpReg[aggOp.Child('aggregation_operator_fn').value](aggOp, q) - } - - logger.debug(q.toString()) - - return q -} diff --git a/parser/registry/stream_selector_operator_registry/common.js b/parser/registry/stream_selector_operator_registry/common.js deleted file mode 100644 index 1af67897..00000000 --- a/parser/registry/stream_selector_operator_registry/common.js +++ /dev/null @@ -1,10 +0,0 @@ -const { unquoteToken } = require('../common') -/** - * - * @param token {Token} - * @returns {string[]} - */ -module.exports.labelAndVal = (token) => { - const label = token.Child('label').value - return [label, unquoteToken(token)] -} \ No newline at end of file diff --git a/parser/registry/stream_selector_operator_registry/index.js b/parser/registry/stream_selector_operator_registry/index.js deleted file mode 100644 index a07eb2f7..00000000 --- a/parser/registry/stream_selector_operator_registry/index.js +++ /dev/null @@ -1,94 +0,0 @@ -const reg = require('./stream_selector_operator_registry') -const idxReg = require('./stream_selector_indexed_registry') -const { hasExtraLabels } = require('../common') - -module.exports = { - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - '!=': (token, query) => { - if (query.stream) { - return genStream(query, reg.eqStream(token)) - } - if (hasExtraLabels(query)) { - return query.where(reg.neqExtraLabels(token)) - } - if (query.ctx.legacy) { - return reg.simpleAnd(query, reg.neqSimple(token)) - } - return idxReg.indexedAnd(query, idxReg.neqIndexed(token)) - }, - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - '=~': (token, query) => { - if (query.stream) { - return genStream(query, reg.regStream(token)) - } - if (hasExtraLabels(query)) { - return query.where(query, reg.regExtraLabels(token)) - } - if (query.ctx.legacy) { - return reg.simpleAnd(query, reg.regSimple(token)) - } - return idxReg.indexedAnd(query, idxReg.reIndexed(token)) - }, - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - '!~': (token, query) => { - if (query.stream) { - return genStream(query, reg.nregStream(token)) - } - if (hasExtraLabels(query)) { - return query.where(query, reg.nregExtraLabels(token)) - } - if (query.ctx.legacy) { - return reg.simpleAnd(query, reg.nregSimple(token)) - } - return idxReg.indexedAnd(query, idxReg.nreIndexed(token)) - }, - /** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - '=': (token, query) => { - if (query.stream) { - return genStream(query, reg.eqStream(token)) - } - if (hasExtraLabels(query)) { - return query.where(query, reg.eqExtraLabels(token)) - } - if (query.ctx.legacy) { - return reg.simpleAnd(query, reg.eqSimple(token)) - } - return idxReg.indexedAnd(query, idxReg.eqIndexed(token)) - } -} - -/** - * - * @param query {Select} - * @param fn {function({labels: Object}): boolean} - * @returns {Select} - */ -const genStream = (query, fn) => ({ - ...query, - stream: [...(query.stream ? query.stream : []), - /** - * @param stream {DataStream} - */ - (stream) => stream.filter(fn) - ] -}) diff --git a/parser/registry/stream_selector_operator_registry/stream_selector_indexed_registry.js b/parser/registry/stream_selector_operator_registry/stream_selector_indexed_registry.js deleted file mode 100644 index 8d8e1a51..00000000 --- a/parser/registry/stream_selector_operator_registry/stream_selector_indexed_registry.js +++ /dev/null @@ -1,100 +0,0 @@ -const Sql = require('@cloki/clickhouse-sql') -const { labelAndVal } = require('./common') -const { DATABASE_NAME } = require('../../../lib/utils') -class AliasedSelect { - /** - * - * @param sel {Select} - * @param alias {String} - */ - constructor (sel, alias) { - this.sel = sel - this.alias = alias - } - - toString () { - return `(${this.sel}) as ${this.alias}` - } -} - -class Match extends Sql.Raw { - constructor (col, re) { - super('') - this.col = col - this.re = re - } - - toString () { - return `match(${this.col}, ${this.re})` - } -} - -class InSubreq extends Sql.Raw { - constructor (col, sub) { - super() - this.col = col - this.sub = sub - } - - toString () { - return `${this.col} IN ${this.sub}` - } -} - -/** - * - * @param query {Select} - * @param subquery {Select} - * @returns {Select} - */ -module.exports.indexedAnd = (query, subquery) => { - let idxSel = query.with() && query.with().idx_sel ? query.with().idx_sel : null - query.ctx.idxId = (query.ctx.idxId || 0) + 1 - const id = `sel_${query.ctx.idxId}` - if (idxSel) { - idxSel.query.join(new AliasedSelect(subquery, Sql.quoteTerm(id)), ' inner any ', - Sql.Eq('sel_1.fingerprint', Sql.quoteTerm(`${id}.fingerprint`))) - return query - } - idxSel = new Sql.With('idx_sel', (new Sql.Select()) - .select(`${id}.fingerprint`) - .from([subquery, id]), query.ctx.inline) - return query.with(idxSel) - .where(new InSubreq('samples.fingerprint', new Sql.WithReference(idxSel))) -} - -/** - * - * @param token {Token} - * @param fn {function(String, any): Object} Sql.Eq, Sql.Neq etc - * @param formatVal? {function(string): Object} Sql.quoteVal or smth - * @returns {Select} - */ -const processIndexed = (token, fn, formatVal) => { - const [label, val] = labelAndVal(token) - formatVal = formatVal || Sql.quoteVal - return (new Sql.Select()).select('fingerprint') - .from(`${DATABASE_NAME()}.time_series_gin`) - .where(Sql.And(Sql.Eq('key', Sql.quoteVal(label)), fn('val', formatVal(val)))) -} - -/** - * - * @param token {Token} - * @returns {Select} - */ -module.exports.eqIndexed = (token) => { - return processIndexed(token, Sql.Eq) -} - -module.exports.neqIndexed = (token) => { - return processIndexed(token, Sql.Ne) -} - -module.exports.reIndexed = (token) => { - return processIndexed(token, (col, val) => Sql.Eq(new Match(col, val), 1)) -} - -module.exports.nreIndexed = (token) => { - return processIndexed(token, (col, val) => Sql.Eq(new Match(col, val), 0)) -} diff --git a/parser/registry/stream_selector_operator_registry/stream_selector_operator_registry.js b/parser/registry/stream_selector_operator_registry/stream_selector_operator_registry.js deleted file mode 100644 index 95975c7b..00000000 --- a/parser/registry/stream_selector_operator_registry/stream_selector_operator_registry.js +++ /dev/null @@ -1,241 +0,0 @@ -const { isEOF, sharedParamNames } = require('../common') -const { labelAndVal } = require('./common') -const Sql = require('@cloki/clickhouse-sql') -/** - * @param regex {boolean} - * @param eq {boolean} - * @param label {string} - * @param value {string} - * @returns {Conditions} - */ -function selectorClauses (regex, eq, label, value) { - const call = regex - ? [new Sql.Raw(`match(arrayFirst(x -> x.1 == ${Sql.quoteVal(label)}, labels).2, ${Sql.quoteVal(value)})`), - 0, eq ? Sql.Ne : Sql.Eq] - : [new Sql.Raw(`arrayFirst(x -> x.1 == ${Sql.quoteVal(label)}, labels).2`), value, eq ? Sql.Eq : Sql.Ne] - return Sql.And( - Sql.Eq(new Sql.Raw(`arrayExists(x -> x.1 == ${Sql.quoteVal(label)}, labels)`), 1), - call[2](call[0], call[1]) - ) -} - -function simpleSelectorClauses (regex, eq, label, value) { - const call = regex - ? [new Sql.Raw(`extractAllGroups(JSONExtractString(labels, ${Sql.quoteVal(label)}), ${Sql.quoteVal('(' + value + ')')})`), - '[]', eq ? Sql.Ne : Sql.Eq] - : [new Sql.Raw(`JSONExtractString(labels, ${Sql.quoteVal(label)})`), value, eq ? Sql.Eq : Sql.Ne] - return Sql.And( - Sql.Eq(new Sql.Raw(`JSONHas(labels, ${Sql.quoteVal(label)})`), 1), - call[2](call[0], call[1]) - ) /* [ - `JSONHas(labels, '${label}')`, - regex - ? `extractAllGroups(JSONExtractString(labels, '${label}'), '(${value})') ${eq ? '!=' : '=='} []` - : `JSONExtractString(labels, '${label}') ${eq ? '=' : '!='} '${value}'` - ] */ -} - -/** - * @param query {Select} - * @returns {With} - */ -const streamSelectQuery = (query) => { - const param = query.getParam(sharedParamNames.timeSeriesTable) || - new Sql.Parameter(sharedParamNames.timeSeriesTable) - query.addParam(param) - const res = new Sql.With( - 'str_sel', - (new Sql.Select()) - .select('fingerprint') - .distinct(true) - .from(param), query.ctx.inline) - if (query.with() && query.with().idx_sel) { - res.query = res.query.where(new Sql.In('fingerprint', 'in', new Sql.WithReference(query.with().idx_sel))) - } - return res -} - -/** - * @param query {Select} - * @param clauses {Conditions | string[]} - * @returns {Select} - */ -module.exports.simpleAnd = (query, clauses) => { - const isStrSel = query.with() && query.with().str_sel - /** - * @type {With} - */ - const strSel = isStrSel ? query.with().str_sel : streamSelectQuery(query) - if (Array.isArray(clauses)) { - strSel.query.where(...clauses) - } else { - strSel.query.where(clauses) - } - query.with(strSel) - /* query.joins = query.joins.filter(j => j.table[1] !== 'time_series') - query.join([new Sql.WithReference(strSel), 'time_series'], 'left', - Sql.Eq('samples.fingerprint', Sql.quoteTerm('time_series.fingerprint'))) */ - if (!isStrSel) { - query.where(new Sql.In('samples.fingerprint', 'in', - (new Sql.Select()).select('fingerprint').from(new Sql.WithReference(strSel)) - )) - } - return query -} - -/** - * - * @param token {Token} - * //@param query {registry_types.Request} - * @returns {Conditions} - */ -module.exports.neqSimple = (token/*, query */) => { - const [label, value] = labelAndVal(token) - return simpleSelectorClauses(false, false, label, value) -} - -/** - * - * @param token {Token} - * //@param query {registry_types.Request} - * @returns {Conditions} - */ -module.exports.neqExtraLabels = (token/*, query */) => { - const [label, value] = labelAndVal(token) - return Sql.Or( - new Sql.Ne(new Sql.Raw(`arrayExists(x -> x.1 == ${Sql.quoteVal(label)} AND x.2 != ${Sql.quoteVal(value)}, extra_labels)`), 0), - Sql.And( - Sql.Eq(new Sql.Raw(`arrayExists(x -> x.1 == ${Sql.quoteVal(label)}, extra_labels)`), 0), - selectorClauses(false, false, label, value) - )) -} - -/** - * - * @param token {Token} - * //@param query {registry_types.Request} - * @returns {function({labels: Object}): boolean} - */ -module.exports.neqStream = (token/*, query */) => { - const [label, value] = labelAndVal(token) - return (e) => isEOF(e) || (e && e.labels && e.labels[label] && e.labels[label] !== value) -} - -/** - * - * @param token {Token} - * //@param query {registry_types.Request} - * @returns {Conditions} - */ -module.exports.nregSimple = (token/*, query */) => { - const [label, value] = labelAndVal(token) - return simpleSelectorClauses(true, false, label, value) -} - -/** - * - * @param token {Token} - * //@param query {registry_types.Request} - * @returns {Conditions} - */ -module.exports.nregExtraLabels = (token/*, query */) => { - const [label, value] = labelAndVal(token) - return Sql.Or( - Sql.Ne( - new Sql.Raw( - `arrayExists(x -> x.1 == '${label}' AND match(x.2, '${value}') == 0, extra_labels)`), 0 - ), - Sql.And( - Sql.Eq(new Sql.Raw(`arrayExists(x -> x.1 == ${Sql.quoteVal(label)}, extra_labels)`), 0), - selectorClauses(true, true, label, value))) -} - -/** - * - * @param token {Token} - * //@param query {registry_types.Request} - * @returns {function({labels: Object}): boolean} - */ -module.exports.nregStream = (token/*, query */) => { - const [label, value] = labelAndVal(token) - const re = new RegExp(value) - return (e) => isEOF(e) || (e && e.labels && e.labels[label] && !e.labels[label].match(re)) -} - -/** - * - * @param token {Token} - * //@param query {registry_types.Request} - * @returns {Conditions} - */ -module.exports.regSimple = (token/*, query */) => { - const [label, value] = labelAndVal(token) - return simpleSelectorClauses(true, true, label, value) -} - -/** - * - * @param token {Token} - * //@param query {registry_types.Request} - * @returns {Conditions} - */ -module.exports.regExtraLabels = (token/*, query */) => { - const [label, value] = labelAndVal(token) - - return Sql.Or( - Sql.Ne( - new Sql.Raw( - `arrayExists(x -> x.1 == '${label}' AND match(x.2, '${value}') != 0, extra_labels)`), 0 - ), - Sql.And(`arrayExists(x -> x.1 == '${label}', extra_labels) == 0`, - selectorClauses(true, true, label, value))) -} - -/** - * - * @param token {Token} - * //@param query {registry_types.Request} - * @returns {function({labels: Object}): boolean} - */ -module.exports.regStream = (token/*, query */) => { - const [label, value] = labelAndVal(token) - const re = new RegExp(value) - return (e) => isEOF(e) || (e && e.labels && e.labels[label] && e.labels[label].match(re)) -} - -/** - * - * @param token {Token} - * //@param query {registry_types.Request} - * @returns {Conditions} - */ -module.exports.eqSimple = (token/*, query */) => { - const [label, value] = labelAndVal(token) - return simpleSelectorClauses(false, true, label, value) -} -/** - * - * @param token {Token} - * //@param query {registry_types.Request} - * @returns {Conditions} - */ -module.exports.eqExtraLabels = (token/*, query */) => { - const [label, value] = labelAndVal(token) - - return Sql.Or( - Sql.Gt(new Sql.Raw(`indexOf(extra_labels, (${Sql.quoteVal(label)}, ${Sql.quoteVal(value)}))`), 0), - Sql.And( - Sql.Eq(new Sql.Raw(`arrayExists(x -> x.1 == ${Sql.quoteVal(label)}, extra_labels)`), 0), - selectorClauses(false, true, label, value))) -} - -/** - * - * @param token {Token} - * //@param query {registry_types.Request} - * @returns {function({labels: Object}): boolean} - */ -module.exports.eqStream = (token/*, query */) => { - const [label, value] = labelAndVal(token) - return (e) => isEOF(e) || (e && e.labels && e.labels[label] && e.labels[label] === value) -} diff --git a/parser/registry/unwrap.js b/parser/registry/unwrap.js deleted file mode 100644 index 4d4cc8fc..00000000 --- a/parser/registry/unwrap.js +++ /dev/null @@ -1,93 +0,0 @@ -const { map, hasExtraLabels, hasStream, addStream } = require('./common') -const Sql = require('@cloki/clickhouse-sql') -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports = (token, query) => { - const label = token.Child('label').value - if (hasStream(query)) { - return viaStream(label, query) - } - if (label === '_entry') { - return unwrapLine(query) - } - if (hasExtraLabels(query)) { - return viaQueryWithExtraLabels(label, query) - } - return viaQuery(label, query) -} - -/** - * - * @param query {Select} - * @returns {Select} - */ -function unwrapLine (query) { - return query.select([new Sql.Raw('toFloat64OrNull(string)'), 'unwrapped']) - .where(Sql.Eq(new Sql.Raw('isNotNull(unwrapped)'), 1)) -} - -/** - * - * @param label {string} - * @param query {Select} - * @returns {Select} - */ -function viaQuery (label, query) { - query.limit(undefined, undefined) - return query.select( - [new Sql.Raw(`toFloat64OrNull(arrayFirst(x -> x.1 == '${label}', labels).2)`), 'unwrapped'] - ).where(Sql.And( - Sql.Eq(new Sql.Raw(`arrayExists(x -> x.1 == '${label}', labels)`), 1), - Sql.Eq(new Sql.Raw('isNotNull(unwrapped)'), 1) - )) -} - -/** - * - * @param label {string} - * @param query {Select} - * @returns {Select} - */ -function viaQueryWithExtraLabels (label, query) { - query.limit(undefined, undefined) - return query.select( - [new Sql.Raw(`toFloat64OrNull(if(arrayExists(x -> x.1 == '${label}', extra_labels), ` + - `arrayFirst(x -> x.1 == '${label}', extra_labels).2, ` + - `arrayFirst(x -> x.1 == '${label}', labels).2))`), 'unwrapped'] - ).where(Sql.And(Sql.Or( - Sql.Ne(new Sql.Raw(`arrayFirstIndex(x -> x.1 == '${label}', extra_labels)`), 0), - Sql.Eq(new Sql.Raw(`arrayExists(x -> x.1 == '${label}', labels)`), 1) - ), Sql.Eq(new Sql.Raw('isNotNull(unwrapped)'), 1))) -} - -/** - * - * @param label {string} - * @param query {Select} - * @returns {Select} - */ -function viaStream (label, query) { - query.limit(undefined, undefined) - const isUnwrapString = label === '_entry' - return addStream(query, (stream) => map(stream, e => { - if (!e || !e.labels) { - return { ...e } - } - if (!isUnwrapString && !e.labels[label]) { - return null - } - try { - e.unwrapped = parseFloat(isUnwrapString ? e.string : e.labels[label]) - if (isNaN(e.unwrapped)) { - return null - } - return e - } catch (e) { - return null - } - }).filter(e => e)) -} diff --git a/parser/registry/unwrap_registry/index.js b/parser/registry/unwrap_registry/index.js deleted file mode 100644 index e3415ace..00000000 --- a/parser/registry/unwrap_registry/index.js +++ /dev/null @@ -1,138 +0,0 @@ -const reg = require('./unwrap_registry') -const { getPlugins, hasStream } = require('../common') - -module.exports = { - /** - * rate(unwrapped-range): calculates per second rate of all values in the specified interval. - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - rate: (token, query) => { - if (hasStream(query)) { - return reg.rate.viaStream(token, query) - } - return reg.rate.viaRequest(token, query) - }, - /** - * sumOverTime(unwrapped-range): the sum of all values in the specified interval. - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - sum_over_time: (token, query) => { - if (hasStream(query)) { - return reg.sumOverTime.viaStream(token, query) - } - return reg.sumOverTime.viaRequest(token, query) - }, - /** - * avgOverTime(unwrapped-range): the average value of all points in the specified interval. - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - avg_over_time: (token, query) => { - if (hasStream(query)) { - return reg.avgOverTime.viaStream(token, query) - } - return reg.avgOverTime.viaRequest(token, query) - }, - /** - * maxOverTime(unwrapped-range): the maximum value of all points in the specified interval. - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - max_over_time: (token, query) => { - if (hasStream(query)) { - return reg.maxOverTime.viaStream(token, query) - } - return reg.maxOverTime.viaRequest(token, query) - }, - /** - * minOverTime(unwrapped-range): the minimum value of all points in the specified interval - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - min_over_time: (token, query) => { - if (hasStream(query)) { - return reg.minOverTime.viaStream(token, query) - } - return reg.minOverTime.viaRequest(token, query) - }, - /** - * first_over_time(unwrapped-range): the first value of all points in the specified interval - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - first_over_time: (token, query) => { - if (hasStream(query)) { - return reg.firstOverTime.viaStream(token, query) - } - return reg.firstOverTime.viaRequest(token, query) - }, - /** - * lastOverTime(unwrapped-range): the last value of all points in the specified interval - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - last_over_time: (token, query) => { - if (hasStream(query)) { - return reg.lastOverTime.viaStream(token, query) - } - return reg.lastOverTime.viaRequest(token, query) - }, - /** - * stdvarOverTime(unwrapped-range): the population standard variance of the values in the specified interval. - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - stdvar_over_time: (token, query) => { - if (hasStream(query)) { - return reg.stdvarOverTime.viaStream(token, query) - } - return reg.stdvarOverTime.viaRequest(token, query) - }, - /** - * stddevOverTime(unwrapped-range): the population standard deviation of the values in the specified interval. - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - stddev_over_time: (token, query) => { - if (hasStream(query)) { - return reg.stddevOverTime.viaStream(token, query) - } - return reg.stddevOverTime.viaRequest(token, query) - }, - /** - * absentOverTime(unwrapped-range): returns an empty vector if the range vector passed to it has any elements and a 1-element vector with the value 1 if the range vector passed to it has no elements. (absentOverTime is useful for alerting on when no time series and logs stream exist for label combination for a certain amount of time.) - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - absent_over_time: (token, query) => { - if (hasStream(query)) { - return reg.absentOverTime.viaStream(token, query) - } - return reg.absentOverTime.viaRequest(token, query) - }, - - ...getPlugins('unwrap_registry', (plugin) => { - return (token, query) => { - return reg.applyViaStream( - token, - query, - plugin.run, - plugin.approx, - false, - 'by_without_unwrap' - ) - } - }) -} diff --git a/parser/registry/unwrap_registry/unwrap_registry.js b/parser/registry/unwrap_registry/unwrap_registry.js deleted file mode 100644 index 505b02eb..00000000 --- a/parser/registry/unwrap_registry/unwrap_registry.js +++ /dev/null @@ -1,231 +0,0 @@ -const { getDuration, concatLabels, timeShiftViaStream } = require('../common') -const _applyViaStream = require('../common').applyViaStream -const Sql = require('@cloki/clickhouse-sql') -/** - * - * @param viaRequest {function(Token, Select): Select} - * @param viaStream {function(Token, Select): Select} - * @returns {{ - * viaRequest: (function(Token, Select): Select), - * viaStream: (function(Token, Select): Select) - * }} - */ -function builder (viaRequest, viaStream) { - return { - viaRequest: viaRequest, - viaStream: viaStream - } -} - -/** - * - * @param token {Token} - * @param query {Select} - * @param counterFn {function(any, any, number): any} - * @param summarizeFn {function(any): number} - * @param lastValue {boolean} if the applier should take the latest value in step (if step > duration) - * @param byWithoutName {string} name of the by_without token -*/ -const applyViaStream = (token, query, counterFn, summarizeFn, lastValue, byWithoutName) => { - return _applyViaStream(token, timeShiftViaStream(token, query), counterFn, summarizeFn, lastValue, byWithoutName) -} - -/** - * sum_over_time(unwrapped-range): the sum of all values in the specified interval. - * @param token {Token} - * @param query {Select} - * @returns {SQLObject} - */ -function applyByWithoutLabels (token, query) { - let labels = concatLabels(query) - const filterLabels = token.Children('label').map(l => l.value).map(l => `'${l}'`) - if (token.Child('by_without_unwrap').value === 'by') { - labels = `arraySort(arrayFilter(x -> arrayExists(y -> x.1 == y, [${filterLabels.join(',')}]) != 0, ` + - `${labels}))` - } - if (token.Child('by_without_unwrap').value === 'without') { - labels = `arraySort(arrayFilter(x -> arrayExists(y -> x.1 == y, [${filterLabels.join(',')}]) == 0, ` + - `${labels}))` - } - return new Sql.Raw(labels) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @param valueExpr {string} - * @param lastValue {boolean | undefined} if the applier should take the latest value in step (if step > duration) - * @returns {Select} - */ -function applyViaRequest (token, query, valueExpr, lastValue) { - valueExpr = new Sql.Raw(valueExpr) - const labels = token.Child('by_without_unwrap') - ? applyByWithoutLabels(token.Child('req_by_without_unwrap'), query) - : concatLabels(query) - const duration = getDuration(token, query) - query.ctx.matrix = true - query.ctx.duration = duration - query.limit(undefined, undefined) - const step = query.ctx.step - const tsMoveParam = new Sql.Parameter('timestamp_shift') - query.addParam(tsMoveParam) - const tsGroupingExpr = new Sql.Raw('') - tsGroupingExpr.toString = () => { - if (!tsMoveParam.get()) { - return `intDiv(timestamp_ns, ${duration}) * ${duration}` - } - return `intDiv(timestamp_ns - ${tsMoveParam.toString()}, ${duration}) * ${duration} + ${tsMoveParam.toString()}` - } - const uwRateA = new Sql.With('uw_rate_a', query) - const groupingQuery = (new Sql.Select()) - .select( - [labels, 'labels'], - [valueExpr, 'value'], - [tsGroupingExpr, 'timestamp_ns'] - ).from(new Sql.WithReference(uwRateA)) - .groupBy('labels', 'timestamp_ns') - .orderBy('labels', 'timestamp_ns') - if (step <= duration) { - return groupingQuery.with(uwRateA) - } - const groupingQueryWith = new Sql.With('uw_rate_b', groupingQuery) - return (new Sql.Select()) - .with(uwRateA, groupingQueryWith) - .select('labels', - [new Sql.Raw(`intDiv(timestamp_ns, ${step}) * ${step}`), 'timestamp_ns'], - [new Sql.Raw('argMin(uw_rate_b.value, uw_rate_b.timestamp_ns)'), 'value'] - ) - .from(new Sql.WithReference(groupingQueryWith)) - .groupBy('labels', 'timestamp_ns') - .orderBy(['labels', 'asc'], ['timestamp_ns', 'asc']) -} - -module.exports = { - applyViaStream: _applyViaStream, - rate: builder((token, query) => { - const duration = getDuration(token, query) - return applyViaRequest(token, query, `SUM(unwrapped) / ${duration / 1000}`) - }, (token, query) => { - const duration = getDuration(token, query) - return applyViaStream(token, query, - (sum, val) => sum + val.unwrapped, - (sum) => sum / duration * 1000, false, 'by_without_unwrap') - }), - - /** - * sum_over_time(unwrapped-range): the sum of all values in the specified interval. - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - sumOverTime: builder((token, query) => { - return applyViaRequest(token, query, 'sum(unwrapped)') - }, (token, query) => { - return applyViaStream(token, query, - (sum, val) => sum + val.unwrapped, - (sum) => sum, false, 'by_without_unwrap') - }), - - /** - * avg_over_time(unwrapped-range): the average value of all points in the specified interval. - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - avgOverTime: builder((token, query) => { - return applyViaRequest(token, query, 'avg(unwrapped)') - }, (token, query) => { - return applyViaStream(token, query, (sum, val) => { - return sum ? { count: sum.count + 1, val: sum.val + val.unwrapped } : { count: 1, val: val.unwrapped } - }, (sum) => sum.val / sum.count, false, 'by_without_unwrap') - }), - /** - * max_over_time(unwrapped-range): the maximum value of all points in the specified interval. - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - maxOverTime: builder((token, query) => { - return applyViaRequest(token, query, 'max(unwrapped)') - }, (token, query) => { - return applyViaStream(token, query, (sum, val) => { - return Math.max(sum, val.unwrapped) - }, (sum) => sum, false, 'by_without_unwrap') - }), - /** - * min_over_time(unwrapped-range): the minimum value of all points in the specified interval - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - minOverTime: builder((token, query) => { - return applyViaRequest(token, query, 'min(unwrapped)') - }, (token, query) => { - return applyViaStream(token, query, (sum, val) => { - return Math.min(sum, val.unwrapped) - }, (sum) => sum, false, 'by_without_unwrap') - }), - /** - * firstOverTime(unwrapped-range): the first value of all points in the specified interval - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - firstOverTime: builder((token, query) => { - return applyViaRequest(token, query, 'argMin(unwrapped, uw_rate_a.timestamp_ns)') - }, (token, query) => { - return applyViaStream(token, query, (sum, val, time) => { - return sum && sum.time < time ? sum : { time: time, first: val.unwrapped } - }, (sum) => sum.first, false, 'by_without_unwrap') - }), - /** - * lastOverTime(unwrapped-range): the last value of all points in the specified interval - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - lastOverTime: builder((token, query) => { - return applyViaRequest(token, query, 'argMax(unwrapped, uw_rate_a.timestamp_ns)', true) - }, (token, query) => { - return applyViaStream(token, query, (sum, val, time) => { - return sum && sum.time > time ? sum : { time: time, first: val.unwrapped } - }, (sum) => sum.first, false, 'by_without_unwrap') - }), - /** - * stdvarOverTime(unwrapped-range): the population standard variance of the values in the specified interval. - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - stdvarOverTime: builder((token, query) => { - return applyViaRequest(token, query, 'varPop(unwrapped)') - }, (token, query) => { - return applyViaStream(token, query, (/* sum, val */) => { - throw new Error('not implemented') - }, (sum) => sum, false, 'by_without_unwrap') - }), - /** - * stddevOverTime(unwrapped-range): the population standard deviation of the values in the specified interval. - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ - stddevOverTime: builder((token, query) => { - return applyViaRequest(token, query, 'stddevPop(unwrapped)') - }, (token, query) => { - return applyViaStream(token, query, (/* sum, val */) => { - throw new Error('not implemented') - }, (sum) => sum, false, 'by_without_unwrap') - }), - /** - * absentOverTime(unwrapped-range): returns an empty vector if the range vector passed to it has any elements and a 1-element vector with the value 1 if the range vector passed to it has no elements. (absentOverTime is useful for alerting on when no time series and logs stream exist for label combination for a certain amount of time.) - * //@param token {Token} - * //@param query {Select} - * @returns {Select} - */ - absentOverTime: (/* token, query */) => { - throw new Error('Not implemented') - }, - applyByWithoutLabels -} diff --git a/parser/transpiler.js b/parser/transpiler.js deleted file mode 100644 index 8da0b920..00000000 --- a/parser/transpiler.js +++ /dev/null @@ -1,625 +0,0 @@ -const streamSelectorOperatorRegistry = require('./registry/stream_selector_operator_registry') -const lineFilterOperatorRegistry = require('./registry/line_filter_operator_registry') -const logRangeAggregationRegistry = require('./registry/log_range_aggregation_registry') -const highLevelAggregationRegistry = require('./registry/high_level_aggregation_registry') -const numberOperatorRegistry = require('./registry/number_operator_registry') -const parameterizedAggregationRegistry = require('./registry/parameterized_aggregation_registry') -const parameterizedUnwrappedRegistry = require('./registry/parameterized_unwrapped_registry') -const complexLabelFilterRegistry = require('./registry/complex_label_filter_expression') -const lineFormat = require('./registry/line_format') -const parserRegistry = require('./registry/parser_registry') -const unwrap = require('./registry/unwrap') -const unwrapRegistry = require('./registry/unwrap_registry') -const { durationToMs, sharedParamNames, getStream, preJoinLabels } = require('./registry/common') -const compiler = require('./bnf') -const { - parseMs, - DATABASE_NAME, - samplesReadTableName, - samplesTableName, - checkVersion, - parseDurationSecOrDefault -} = require('../lib/utils') -const { getPlg } = require('../plugins/engine') -const Sql = require('@cloki/clickhouse-sql') -const { simpleAnd } = require('./registry/stream_selector_operator_registry/stream_selector_operator_registry') -const logger = require('../lib/logger') -const { QrynBadRequest } = require('../lib/handlers/errors') -const optimizations = require('./registry/smart_optimizations') -const clusterName = require('../common').clusterName -const dist = clusterName ? '_dist' : '' -const wasm = require('../wasm_parts/main') -const { bothType, logType, metricType } = require('../common') - -/** - * @param joinLabels {boolean} - * @param types {[number]} - * @returns {Select} - */ -module.exports.initQuery = (joinLabels, types) => { - types = types || [bothType, logType] - const samplesTable = new Sql.Parameter(sharedParamNames.samplesTable) - const timeSeriesTable = new Sql.Parameter(sharedParamNames.timeSeriesTable) - const from = new Sql.Parameter(sharedParamNames.from) - const to = new Sql.Parameter(sharedParamNames.to) - const limit = new Sql.Parameter(sharedParamNames.limit) - const matrix = new Sql.Parameter('isMatrix') - limit.set(2000) - const tsClause = new Sql.Raw('') - tsClause.toString = () => { - if (to.get()) { - return Sql.between('samples.timestamp_ns', from, to).toString() - } - return Sql.Gt('samples.timestamp_ns', from).toString() - } - const tsGetter = new Sql.Raw('') - tsGetter.toString = () => { - if (matrix.get()) { - return 'intDiv(samples.timestamp_ns, 1000000)' - } - return 'samples.timestamp_ns' - } - - const q = (new Sql.Select()) - .select(['samples.string', 'string'], - ['samples.fingerprint', 'fingerprint'], [tsGetter, 'timestamp_ns']) - .from([samplesTable, 'samples']) - .orderBy(['timestamp_ns', 'desc']) - .where(Sql.And( - tsClause, - new Sql.In('samples.type', 'in', types))) - .limit(limit) - .addParam(samplesTable) - .addParam(timeSeriesTable) - .addParam(from) - .addParam(to) - .addParam(limit) - .addParam(matrix) - return q -} - -/** - * @param joinLabels {boolean} - * @param types {[number] || undefined} - * @returns {Select} - */ -/*module.exports.initQueryV3_2 = (joinLabels, types) => { - types = types || [bothType, logType] - const from = new Sql.Parameter(sharedParamNames.from) - const to = new Sql.Parameter(sharedParamNames.to) - const tsClause = new Sql.Raw('') - tsClause.toString = () => { - if (to.get()) { - return Sql.between('samples.timestamp_ns', from, to).toString() - } - return Sql.Gt('samples.timestamp_ns', from).toString() - } - const q = (new Sql.Select()) - .select(['samples.fingerprint', 'fingerprint']) - .from(['metrics_15s', 'samples']) - .where(Sql.And( - tsClause, - new Sql.In('samples.type', 'in', types))) - .addParam(from) - .addParam(to) - if (joinLabels) { - //TODO: fix join - q.join(new Aliased(`${DATABASE_NAME()}.time_series${dist}`, 'time_series'), 'left any', - Sql.Eq('samples.fingerprint', new Sql.Raw('time_series.fingerprint'))) - q.select([new Sql.Raw('JSONExtractKeysAndValues(time_series.labels, \'String\')'), 'labels']) - } - return q -}*/ - -/** - * - * @param request {{ - * query: string, - * limit: number, - * direction: string, - * start: string, - * end: string, - * step: string, - * stream?: (function(DataStream): DataStream)[], - * rawQuery: boolean - * }} - * @returns {{query: string, stream: (function (DataStream): DataStream)[], matrix: boolean, duration: number | undefined}} - */ -module.exports.transpile = (request) => { - const response = (query) => ({ - query: request.rawQuery ? query : query.toString(), - matrix: !!query.ctx.matrix, - duration: query.ctx && query.ctx.duration ? query.ctx.duration : 1000, - stream: getStream(query) - }) - const expression = compiler.ParseScript(request.query.trim()) - if (!expression) { - throw new QrynBadRequest('invalid request') - } - const token = expression.rootToken - if (token.Child('user_macro')) { - return module.exports.transpile({ - ...request, - query: module.exports.transpileMacro(token.Child('user_macro')) - }) - } - - let start = parseMs(request.start, Date.now() - 3600 * 1000) - let end = parseMs(request.end, Date.now()) - const step = request.step ? Math.floor(parseDurationSecOrDefault(request.step, 5) * 1000) : 0 - /* - let start = BigInt(request.start || (BigInt(Date.now() - 3600 * 1000) * BigInt(1e6))) - let end = BigInt(request.end || (BigInt(Date.now()) * BigInt(1e6))) - const step = BigInt(request.step ? Math.floor(parseFloat(request.step) * 1000) : 0) * BigInt(1e6) - */ - if (request.optimizations) { - const query = optimizations.apply(token, start * 1000000, end * 1000000, step * 1000000) - if (query) { - return response(query) - } - } - const joinLabels = ['unwrap_function', 'log_range_aggregation', 'aggregation_operator', - 'agg_statement', 'user_macro', 'parser_expression', 'label_filter_pipeline', - 'line_format_expression', 'labels_format_expression', 'summary'].some(t => token.Child(t)) - let query = module.exports.initQuery(joinLabels, - token.Child('unwrap_value_statement') ? [bothType, metricType] : undefined) - let limit = request.limit ? request.limit : 2000 - const order = request.direction === 'forward' ? 'asc' : 'desc' - query.orderBy(...query.orderBy().map(o => [o[0], order])) - const readTable = samplesReadTableName(start) - query.ctx = { - step: step, - legacy: !checkVersion('v3_1', start), - joinLabels: joinLabels, - inline: !!clusterName - } - let duration = null - let matrixOp = [ - 'aggregation_operator', - 'unwrap_function', - 'log_range_aggregation', - 'parameterized_unwrapped_expression'].find(t => token.Child(t)) - if (matrixOp) { - duration = durationToMs(token.Child(matrixOp).Child('duration_value').value) - start = Math.floor(start / duration) * duration - end = Math.ceil(end / duration) * duration - query.ctx = { - ...query.ctx, - start, - end - } - } - joinLabels && doStreamSelectorOperatorRegistry(token, query) - joinLabels && preJoinLabels(token, query) - matrixOp = matrixOp || (token.Child('summary') && 'summary') - switch (matrixOp) { - case 'aggregation_operator': - query = module.exports.transpileAggregationOperator(token, query) - break - case 'unwrap_function': - query = module.exports.transpileUnwrapFunction(token, query) - break - case 'log_range_aggregation': - query = module.exports.transpileLogRangeAggregation(token, query) - break - case 'parameterized_unwrapped_expression': - query = module.exports.transpileParameterizedUnwrappedExpression(token, query) - break - case 'summary': - query.ctx.matrix = false - query = module.exports.transpileSummary(token, query, request.limit || 2000) - setQueryParam(query, sharedParamNames.limit, undefined) - break - default: - // eslint-disable-next-line no-case-declarations - const _query = module.exports.transpileLogStreamSelector(token, query) - // eslint-disable-next-line no-case-declarations - const wth = new Sql.With('sel_a', _query) - query = (new Sql.Select()) - .with(wth) - .from(new Sql.WithReference(wth)) - .orderBy(['labels', order], ['timestamp_ns', order]) - setQueryParam(query, sharedParamNames.limit, limit) - if (!joinLabels) { - query.from([new Sql.WithReference(query.with().sel_a), 'samples']) - preJoinLabels(token, query, dist) - query.select(new Sql.Raw('samples.*')) - } - } - if (token.Child('agg_statement') && token.Child('compared_agg_statement_cmp')) { - const op = token.Child('compared_agg_statement_cmp').Child('number_operator').value - query = numberOperatorRegistry[op](token.Child('agg_statement'), query) - } - if (token.Child('parameterized_expression')) { - const op = token.Child('parameterized_expression_fn').value - query = parameterizedAggregationRegistry[op](token.Child('parameterized_expression'), query) - } - setQueryParam(query, sharedParamNames.timeSeriesTable, `${DATABASE_NAME()}.time_series`) - setQueryParam(query, sharedParamNames.samplesTable, `${DATABASE_NAME()}.${readTable}${dist}`) - setQueryParam(query, sharedParamNames.from, start + '000000') - setQueryParam(query, sharedParamNames.to, end + '000000') - setQueryParam(query, 'isMatrix', query.ctx.matrix) - console.log(query.toString()) - return { - query: request.rawQuery ? query : query.toString(), - matrix: !!query.ctx.matrix, - duration: query.ctx && query.ctx.duration ? query.ctx.duration : 1000, - stream: getStream(query) - } -} - -/** - * - * @param request {{ - * query: string, - * limit: number, - * direction: string, - * start: string, - * end: string, - * step: string, - * stream?: (function(DataStream): DataStream)[], - * rawQuery: boolean - * }} - * @returns {{query: string, stream: (function (DataStream): DataStream)[], matrix: boolean, duration: number | undefined}} - */ -module.exports.transpileSummaryETL = (request) => { - const expression = compiler.ParseScript(request.query.trim()) - const root = expression.rootToken - if (!root.Child('summary')) { - throw new QrynBadRequest('request should be a summary expression') - } - const selector = root.Child('log_stream_selector') - const _request = { - ...request, - query: selector.value, - rawQuery: true - } - const byWithout = root.Child('by_without').value - const labels = "['" + root.Child('label_list').Children('label').map(l => l.value).join("','") + "']" - const exp = byWithout === 'by' ? '== 1' : '!= 1' - - const query = module.exports.transpile(_request) - query.query = (new Sql.Select()) - .select( - [new Sql.Raw(`arrayFilter(x -> has(${labels}, x.1) ${exp}, labels)`), 'labels'], - [new Sql.Raw('cityHash64(labels)'), 'fingerprint'], - 'string', - 'timestamp_ns') - .from(query.query) - return { - ...query, - query: query.query.toString() - } -} - -class Subquery extends Sql.Raw { - constructor (sel) { - super() - this.sel = sel - } - - toString () { - return '(' + this.sel + ')' - } -} - -module.exports.transpileSummary = (token, query, limit) => { - query = module.exports.transpileLogStreamSelector(token.Child('log_stream_selector'), query) - query.limit() - query.ctx = query.ctx || {} - query.ctx.stream = query.ctx.stream || [] - const withQ = new Sql.With('sum_a', query) - const guessLevelCHExp = 'map(\'\', \'unknown\', \'debu\', \'debug\', \'info\', \'info\', \'warn\', \'warning\', \'erro\', \'error\', \'crit\', \'critical\', \'fata\', \'fatal\', \'I\', \'info\', \'W\', \'warning\', \'E\', \'error\', \'F\', \'fatal\')[arrayFirst(x -> notEmpty(x) , [lowerUTF8(arrayMap(x -> x[3], extractAllGroupsVertical(sum_a.string, \'(?i)(^|\\\\s|[\\]);|:,.])([\\[(<\\\']|Level=)?(debu|info|warn|erro|crit|fata)\'))[1]), extract(sum_a.string, \'^([IWEF])[0-9]{4}(\\\\s|\\\\p{P})\')])]' - query = (new Sql.Select()).with(withQ).select( - [query.getParam(sharedParamNames.to), 'timestamp_ns'], - [new Sql.Raw('[(\'level\', _level)]::Array(Tuple(String,String))'), 'labels'], - [new Sql.Raw("format('{} ({}%): {}', toString(_c), toString(round(toFloat64(_c) / _overall * 100, 3)), min(sum_a.string))"), 'string'], - [new Sql.Raw('0'), 'value'], - '_c', - [new Subquery((new Sql.Select()).select(new Sql.Raw('count()')).from(new Sql.WithReference(withQ))), '_overall'] - ).from(new Sql.WithReference(withQ)) - .groupBy(new Sql.Raw( - '(arrayReduce(\'sum\', arrayMap(x -> cityHash64(lowerUTF8(x[2])), extractAllGroupsVertical(sum_a.string, \'(^|\\\\p{P}|\\\\s)([a-zA-Z]+)(\\\\p{P}|$|\\\\s)\')) as a),' + - ' arrayReduce(\'groupBitXor\', a), toUInt64(arrayProduct(arrayMap(x -> x*2+1, a))), ' + guessLevelCHExp + ' as _level)')) - .orderBy([new Sql.Raw('count() as _c'), 'DESC']) - .limit(limit || 2000) - return query -} - -/** - * - * @param query {Select} - * @param name {string} - * @param val {any} - */ -const setQueryParam = (query, name, val) => { - if (query.getParam(name)) { - query.getParam(name).set(val) - } -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.transpileParameterizedUnwrappedExpression = (token, query) => { - query = module.exports.transpileLogStreamSelector(token, query) - if (token.Child('unwrap_value_statement')) { - if (token.Child('log_pipeline')) { - throw new Error('log pipeline not supported') - } - query = transpileUnwrapMetrics(token, query) - } else { - query = module.exports.transpileUnwrapExpression(token.Child('unwrap_expression'), query) - } - return parameterizedUnwrappedRegistry[token.Child('parameterized_unwrapped_expression_fn').value]( - token, query) -} - -/** - * - * @param request {{ - * query: string, - * suppressTime?: boolean, - * stream?: (function(DataStream): DataStream)[], - * samplesTable?: string, - * rawRequest: boolean}} - * @returns {{query: string | registry_types.Request, - * stream: (function(DataStream): DataStream)[]}} - */ -module.exports.transpileTail = (request) => { - const expression = compiler.ParseScript(request.query.trim()) - const denied = ['user_macro', 'aggregation_operator', 'unwrap_function', 'log_range_aggregation'] - for (const d of denied) { - if (expression.rootToken.Child(d)) { - throw new Error(`${d} is not supported. Only raw logs are supported`) - } - } - - let query = module.exports.initQuery(false) - doStreamSelectorOperatorRegistry(expression.rootToken, query) - preJoinLabels(expression.rootToken, query, dist) - query.ctx = { - ...(query.ctx || {}), - legacy: true - } - query = module.exports.transpileLogStreamSelector(expression.rootToken, query) - setQueryParam(query, sharedParamNames.timeSeriesTable, `${DATABASE_NAME()}.time_series`) - setQueryParam(query, sharedParamNames.samplesTable, `${DATABASE_NAME()}.${samplesTableName}${dist}`) - setQueryParam(query, sharedParamNames.from, new Sql.Raw('(toUnixTimestamp(now()) - 5) * 1000000000')) - query.order_expressions = [] - query.orderBy(['timestamp_ns', 'asc']) - query.limit(undefined, undefined) - return { - query: request.rawRequest ? query : query.toString(), - stream: getStream(query) - } -} - -/** - * - * @param request {string[]} ['{ts1="a1"}', '{ts2="a2"}', ...] - * @returns {string} clickhouse query - */ -module.exports.transpileSeries = (request) => { - if (request.length === 0) { - return '' - } - /** - * - * @param req {string} - * @returns {Select} - */ - const getQuery = (req) => { - const expression = compiler.ParseScript(req.trim()) - let query = module.exports.transpileLogStreamSelector(expression.rootToken, module.exports.initQuery()) - query = simpleAnd(query, new Sql.Raw('1 == 1')) - const _query = query.withs.str_sel.query - if (query.with() && query.with().idx_sel) { - _query.with(query.withs.idx_sel) - } - _query.params = query.params - _query.columns = [] - return _query.select('labels') - } - class UnionAll extends Sql.Raw { - constructor (sqls) { - super() - this.sqls = [sqls] - } - - toString () { - return this.sqls.map(sql => `(${sql})`).join(' UNION ALL ') - } - } - const query = getQuery(request[0]) - query.withs.idx_sel.query = new UnionAll(query.withs.idx_sel.query) - for (const req of request.slice(1)) { - const _query = getQuery(req) - query.withs.idx_sel.query.sqls.push(_query.withs.idx_sel.query) - } - if (process.env.ADVANCED_SERIES_REQUEST_LIMIT) { - query.limit(process.env.ADVANCED_SERIES_REQUEST_LIMIT) - } - setQueryParam(query, sharedParamNames.timeSeriesTable, `${DATABASE_NAME()}.time_series${dist}`) - setQueryParam(query, sharedParamNames.samplesTable, `${DATABASE_NAME()}.${samplesReadTableName()}${dist}`) - // logger.debug(query.toString()) - return query.toString() -} - -/** - * - * @param token {Token} - * @returns {string} - */ -module.exports.transpileMacro = (token) => { - const plg = Object.values(getPlg({ type: 'macros' })).find(m => token.Child(m._main_rule_name)) - return plg.stringify(token) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.transpileAggregationOperator = (token, query) => { - const agg = token.Child('aggregation_operator') - if (token.Child('log_range_aggregation')) { - query = module.exports.transpileLogRangeAggregation(agg, query) - } else if (token.Child('unwrap_function')) { - query = module.exports.transpileUnwrapFunction(agg, query) - } - return highLevelAggregationRegistry[agg.Child('aggregation_operator_fn').value](token, query) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.transpileLogRangeAggregation = (token, query) => { - const agg = token.Child('log_range_aggregation') - query = module.exports.transpileLogStreamSelector(agg, query) - return logRangeAggregationRegistry[agg.Child('log_range_aggregation_fn').value](token, query) -} - -/** - * - * @param token {Token} - * @param query {Sql.Select} - * @returns {Sql.Select} - */ -module.exports.transpileLogStreamSelector = (token, query) => { - doStreamSelectorOperatorRegistry(token, query) - for (const pipeline of token.Children('log_pipeline')) { - if (pipeline.Child('line_filter_expression')) { - const op = pipeline.Child('line_filter_operator').value - query = lineFilterOperatorRegistry[op](pipeline, query) - continue - } - if (pipeline.Child('parser_expression')) { - const op = pipeline.Child('parser_fn_name').value - query = parserRegistry[op](pipeline, query) - continue - } - if (pipeline.Child('label_filter_pipeline')) { - query = module.exports.transpileLabelFilterPipeline(pipeline.Child('label_filter_pipeline'), query) - continue - } - if (pipeline.Child('line_format_expression')) { - query = lineFormat(pipeline, query) - continue - } - } - for (const c of ['labels_format_expression']) { - if (token.Children(c).length > 0) { - throw new Error(`${c} not supported`) - } - } - return query -} - -/** - * - * @param pipeline {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.transpileLabelFilterPipeline = (pipeline, query) => { - return complexLabelFilterRegistry(pipeline.Child('complex_label_filter_expression'), query) -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.transpileUnwrapFunction = (token, query) => { - query = module.exports.transpileLogStreamSelector(token, query) - if (token.Child('unwrap_value_statement')) { - if (token.Child('log_pipeline')) { - throw new Error('log pipeline not supported') - } - query = transpileUnwrapMetrics(token, query) - } else { - query = module.exports.transpileUnwrapExpression(token.Child('unwrap_expression'), query) - } - return unwrapRegistry[token.Child('unwrap_fn').value](token, query) -} - -/** - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -const transpileUnwrapMetrics = (token, query) => { - query.select_list = query.select_list.filter(f => f[1] !== 'string') - query.select(['value', 'unwrapped']) - return query -} - -/** - * - * @param token {Token} - * @param query {Select} - * @returns {Select} - */ -module.exports.transpileUnwrapExpression = (token, query) => { - return unwrap(token.Child('unwrap_statement'), query) -} - -/** - * - * @param query {Select | registry_types.UnionRequest} - * @returns {string} - */ -module.exports.requestToStr = (query) => { - if (query.requests) { - return query.requests.map(r => `(${module.exports.requestToStr(r)})`).join(' UNION ALL ') - } - let req = query.with - ? 'WITH ' + Object.entries(query.with).filter(e => e[1]) - .map(e => `${e[0]} as (${module.exports.requestToStr(e[1])})`).join(', ') - : '' - req += ` SELECT ${query.distinct ? 'DISTINCT' : ''} ${query.select.join(', ')} FROM ${query.from} ` - for (const clause of query.left_join || []) { - req += ` LEFT JOIN ${clause.name} ON ${whereBuilder(clause.on)}` - } - req += query.where && query.where.length ? ` WHERE ${whereBuilder(query.where)} ` : '' - req += query.group_by ? ` GROUP BY ${query.group_by.join(', ')}` : '' - req += query.having && query.having.length ? ` HAVING ${whereBuilder(query.having)}` : '' - req += query.order_by ? ` ORDER BY ${query.order_by.name.map(n => n + ' ' + query.order_by.order).join(', ')} ` : '' - req += typeof (query.limit) !== 'undefined' ? ` LIMIT ${query.limit}` : '' - req += typeof (query.offset) !== 'undefined' ? ` OFFSET ${query.offset}` : '' - req += query.final ? ' FINAL' : '' - return req -} - -module.exports.stop = () => { - require('./registry/line_format/go_native_fmt').stop() -} - -/** - * - * @param clause {(string | string[])[]} - */ -const whereBuilder = (clause) => { - const op = clause[0] - const _clause = clause.slice(1).map(c => Array.isArray(c) ? `(${whereBuilder(c)})` : c) - return _clause.join(` ${op} `) -} - -const doStreamSelectorOperatorRegistry = (token, query) => { - if (!query.with().idx_sel && !query.with().str_sel) { - const rules = token.Children('log_stream_selector_rule') - for (const rule of rules) { - const op = rule.Child('operator').value - query = streamSelectorOperatorRegistry[op](rule, query) - } - } -} diff --git a/parsers.js b/parsers.js deleted file mode 100644 index 533f1675..00000000 --- a/parsers.js +++ /dev/null @@ -1,392 +0,0 @@ -const { EventEmitter } = require('events') -const { QrynError } = require('./lib/handlers/errors') -const StreamArray = require('stream-json/streamers/StreamArray') -const { parser: jsonlParser } = require('stream-json/jsonl/Parser') -const yaml = require('yaml') -let snappy = null -try { - snappy = require('snappyjs') -} catch (e) {} -const stream = require('stream') -const protobufjs = require('protobufjs') -const path = require('path') -const WriteRequest = protobufjs.loadSync(path.join(__dirname, 'lib', 'prompb.proto')).lookupType('WriteRequest') -const PushRequest = protobufjs.loadSync(path.join(__dirname, 'lib', 'loki.proto')).lookupType('PushRequest') -const OTLPTraceData = protobufjs.loadSync(path.join(__dirname, 'lib', 'otlp.proto')).lookupType('TracesData') -const OTLPLogsData = protobufjs.loadSync(path.join(__dirname, 'lib', 'otlp.proto')).lookupType('LogsData') -const { parse: queryParser } = require('fast-querystring') - -/** - * - * @param req {FastifyRequest} - * @param payload {Stream} - * @returns {any} - */ -const wwwFormParser = async (req, payload) => { - return queryParser(await getContentBody(req, payload)) -} - -/** - * - * @param req {FastifyRequest} - * @param payload {Stream} - */ -const lokiPushJSONParser = async (req, payload) => { - try { - const length = getContentLength(req, 1e9) - if (length > 5 * 1024 * 1024) { - return - } - await shaper.register(length) - const body = await getContentBody(req, payload) - return JSON.parse(body) - } catch (err) { - err.statusCode = 400 - throw err - } -} - -/** - * - * @param req {FastifyRequest} - * @param payload {Stream} - * @returns {any} - */ -async function tempoPushNDJSONParser (req, payload) { - const parser = payload.pipe(jsonlParser()) - parser.on('error', err => { parser.error = err }) - return parser -} - -/** - * @param req {FastifyRequest} - * @param payload {Stream} - * @returns {any} - */ -async function jsonParser (req, payload) { - return JSON.parse(await getContentBody(req, payload)) -} - -/** - * @param req {FastifyRequest} - * @param payload {Stream} - * @returns {any} - */ -async function yamlParser (req, payload) { - return yaml.parse(await getContentBody(req, payload)) -} - -/** - * - * @param req {FastifyRequest} - * @param payload {Stream} - * @returns {any} - */ -async function tempoPushParser (req, payload) { - const firstData = await new Promise((resolve, reject) => { - req.raw.once('data', resolve) - req.raw.once('error', reject) - req.raw.once('close', () => resolve(null)) - req.raw.once('end', () => resolve(null)) - }) - const parser = StreamArray.withParser() - parser.on('error', err => { parser.error = err }) - parser.write(firstData || '[]') - if (!firstData) { - parser.end() - return parser - } - req.raw.pipe(parser) - return parser -} - -/** - * @param req {FastifyRequest} - * @param payload {Stream} - */ -async function rawStringParser (req, payload) { - return await getContentBody(req, payload) -} - -/** - * @param req {FastifyRequest} - * @param payload {Stream} - */ -async function lokiPushProtoParser (req, payload) { - if (!snappy) { - throw new Error('snappy not found') - } - const length = getContentLength(req, 5e6) - await shaper.register(length) - const body = [] - req.raw.on('data', (data) => { - body.push(data) - }) - await new Promise(resolve => req.raw.once('end', resolve)) - let _data = await snappy.uncompress(Buffer.concat(body)) - _data = PushRequest.decode(_data) - _data.streams = _data.streams.map(s => ({ - ...s, - entries: s.entries.map(e => { - const ts = e.timestamp - ? BigInt(e.timestamp.seconds) * BigInt(1e9) + BigInt(e.timestamp.nanos) - : BigInt(Date.now().toString() + '000000') - return { - ...e, - timestamp: ts - } - }) - })) - return _data.streams -} - -/** - * @param req {FastifyRequest} - * @param payload {Stream} - */ -async function prometheusPushProtoParser (req, payload) { - if (!snappy) { - throw new Error('snappy not found') - } - const length = getContentLength(req, 5e6) - await shaper.register(length) - const body = [] - req.raw.on('data', (data) => { - body.push(data) - }) - await new Promise(resolve => req.raw.once('end', resolve)) - let _data = await snappy.uncompress(Buffer.concat(body)) - _data = WriteRequest.decode(_data) - _data.timeseries = _data.timeseries.map(s => ({ - ...s, - samples: s.samples.map(e => { - const nanos = e.timestamp + '000000' - return { - ...e, - timestamp: nanos - } - }) - })) - return _data -} - -/** - * @param req {FastifyRequest} - * @param payload {Stream} zlib.Gunzip - */ -async function otlpPushProtoParser (req, payload) { - const length = getContentLength(req, 5e6) - await shaper.register(length) - let body = [] - const otelStream = stream.Readable.from(payload) - otelStream.on('data', data => { - body.push(data) - }) - await new Promise(resolve => otelStream.once('end', resolve)) - body = Buffer.concat(body) - body = OTLPTraceData.toObject(OTLPTraceData.decode(body), { - longs: String, - bytes: String - }) - return body -} - -/** - * - * @param req {FastifyRequest} - * @param payload {Stream} - * @returns {*} - */ -function tempoNDJsonParser (req, payload) { - const parser = req.raw.pipe(jsonlParser()) - parser.on('error', err => { parser.error = err }) - return parser -} - -/** - * - * @param req {FastifyRequest} - * @param payload {Stream} - * @returns {*} - */ -async function otlpLogsDataParser (req, payload) { - const length = getContentLength(req, 5e6) - await shaper.register(length) - let body = [] - const otelStream = stream.Readable.from(payload) - otelStream.on('data', data => { - body.push(data) - }) - await new Promise(resolve => otelStream.once('end', resolve)) - body = Buffer.concat(body) - body = OTLPLogsData.toObject(OTLPLogsData.decode(body), { - longs: String, - bytes: String - }) - return body -} - -/** - * - * @param subparsers {function(FastifyRequest): Promise<*|undefined>} - * @returns {function(FastifyRequest): Promise<*|undefined>} - */ -function combinedParser (...subparsers) { - /** - * - * @param req {FastifyRequest} - * @returns {any} - */ - return async (req, payload) => { - for (const p of subparsers) { - try { - return await p(req, payload) - } catch (e) {} - } - return undefined - } -} - -const parsers = { - _parsers: {}, - /** - * - * @param fastify {Fastify} - */ - init: (fastify) => { - for (const type of Object.keys(parsers._parsers)) { - fastify.addContentTypeParser(type, parsers.parse(type)) - } - return fastify - }, - - /** - * - * @param contentType {string} - * @returns {function(FastifyRequest, Stream): Promise<*>} - */ - parse: (contentType) => - /** - * - * @param req {FastifyRequest} - * @param payload {Stream} - */ - async (req, payload) => { - const find = (obj, path) => { - for (const p of path) { - if (!obj[p]) { - return null - } - obj = obj[p] - } - return obj - } - const parser = find(parsers._parsers, [contentType, req.routeOptions.method, req.routeOptions.url]) || - find(parsers._parsers, ['*', req.routeOptions.method, req.routeOptions.url]) - if (!parser) { - throw new Error(`undefined parser for ${contentType} ${req.routeOptions.method} ${req.routeOptions.url}`) - } - return await parser(req, payload) - }, - - /** - * - * @param method {string} - * @param route {string} - * @param contentType {string} - * @param parser {function(FastifyRequest): Promise<any>} - */ - register: (method, route, contentType, parser) => { - parsers._parsers[contentType] = parsers._parsers[contentType] || {} - parsers._parsers[contentType][method.toUpperCase()] = parsers._parsers[contentType][method.toUpperCase()] || {} - parsers._parsers[contentType][method.toUpperCase()][route] = parser - } -} - -const shaper = { - onParse: 0, - onParsed: new EventEmitter(), - shapeInterval: setInterval(() => { - shaper.onParse = 0 - shaper.onParsed.emit('parsed') - }, 1000), - /** - * - * @param size {number} - * @returns {Promise<void>} - */ - register: async (size) => { - while (shaper.onParse + size > 50e6) { - await new Promise(resolve => { shaper.onParsed.once('parsed', resolve) }) - } - shaper.onParse += size - }, - stop: () => { - shaper.shapeInterval && clearInterval(shaper.shapeInterval) - shaper.shapeInterval = null - shaper.onParsed.removeAllListeners('parsed') - shaper.onParsed = null - } -} - -/** - * - * @param req {FastifyRequest} - * @param limit {number} - * @returns {number} - */ -function getContentLength (req, limit) { - if (!req.headers['content-length'] || isNaN(parseInt(req.headers['content-length']))) { - return 5 * 1024 * 1024 - } - const res = parseInt(req.headers['content-length']) - if (limit && res > limit) { - throw new QrynError(400, 'Request is too big') - } - return res -} - -/** - * - * @param req {FastifyRequest} - * @param payload {Stream} - * @returns {Promise<string>} - */ -async function getContentBody (req, payload) { - if (req._rawBody) { - return req._rawBody - } - const body = [] - payload.on('data', data => { - body.push(data)// += data.toString() - }) - if (payload.isPaused && payload.isPaused()) { - payload.resume() - } - await new Promise(resolve => { - payload.on('end', resolve) - payload.on('close', resolve) - }) - req._rawBody = Buffer.concat(body).toString() - return Buffer.concat(body).toString() -} - -module.exports = { - getContentBody, - getContentLength, - shaper, - lokiPushJSONParser, - tempoPushParser, - tempoPushNDJSONParser, - jsonParser, - yamlParser, - combinedParser, - rawStringParser, - lokiPushProtoParser, - prometheusPushProtoParser, - tempoNDJsonParser, - otlpPushProtoParser, - wwwFormParser, - parsers, - otlpLogsDataParser -} diff --git a/patches/bnf+1.0.1.patch b/patches/bnf+1.0.1.patch deleted file mode 100644 index b75d14ea..00000000 --- a/patches/bnf+1.0.1.patch +++ /dev/null @@ -1,132 +0,0 @@ -diff --git a/node_modules/bnf/BnfRules.js b/node_modules/bnf/BnfRules.js -index 1f949c1..098e140 100755 ---- a/node_modules/bnf/BnfRules.js -+++ b/node_modules/bnf/BnfRules.js -@@ -19,6 +19,9 @@ exports.bnfRules = { - - return false; - }, -+ REALPHA(token){ -+ return token.TryRe(/^\p{L}/u); -+ }, - SYMBOL( token ){ - if( token.CharIs( 33 ) - || token.CharCodeRange( 35, 38 ) -@@ -35,7 +38,7 @@ exports.bnfRules = { - }, - ANYCHAR( token ){ - return token.Or( [ -- token.Rule( "ALPHA" ), -+ token.Rule( "REALPHA" ), - token.Rule( "DIGIT" ), - token.Rule( "SYMBOL" ), - token.Rule( "ONEWSP" ) -@@ -135,9 +138,13 @@ exports.bnfRules = { - ESCAQUOTE( token ){ - return token.TryString( Buffer.from( [ 92, 96 ] ) ); - }, -+ ESCSLASH(token){ -+ return token.TryString( Buffer.from( [ 92, 92 ] ) ); -+ }, - //DO BE REMOVED IN THIS MAJOR VERSION! DO NOT USE! - SQEANYCHAR( token ){ - return token.Or( [ -+ token.Rule( "ESCSLASH" ), - token.Rule( "ESCSQUOTE" ), - token.Rule( "ANYCHAR" ), - token.Rule( "QUOTE" ), -@@ -162,6 +169,7 @@ exports.bnfRules = { - //DO BE REMOVED IN THIS MAJOR VERSION! DO NOT USE! - QEANYCHAR( token ){ - return token.Or( [ -+ token.Rule( "ESCSLASH" ), - token.Rule( "ESCQUOTE" ), - token.Rule( "ANYCHAR" ), - token.Rule( "SQUOTE" ), -@@ -186,6 +194,7 @@ exports.bnfRules = { - //DO BE REMOVED IN THIS MAJOR VERSION! DO NOT USE! - AQEANYCHAR( token ){ - return token.Or( [ -+ token.Rule( "ESCSLASH" ), - token.Rule( "ESCAQUOTE" ), - token.Rule( "ANYCHAR" ), - token.Rule( "SQUOTE" ), -@@ -219,11 +228,12 @@ exports.bnfRules = { - */ - }, - AQLITERAL( token ){ -- return token.And( [ -+ const res = token.And( [ - token.Rule( "AQUOTE" ), - token.Rule( "AQLITERALCHARS" ), - token.Rule( "AQUOTE" ) - ]); -+ return res; - }, - LITERAL( token ){ - return token.Or( [ -@@ -285,7 +295,7 @@ exports.parserRules = { - } - else{ - //This can be optimized @LHF -- for( let i = 0; i < token._tokenTrees.length - 1; i++ ){ -+ /*for( let i = 0; i < token._tokenTrees.length - 1; i++ ){ - for( let t = 0; t < token._tokenTrees[i].length; t++ ){ - for( let line in token._tokenTrees[i][t].expected ){ - for( let char in token._tokenTrees[i][t].expected[line] ){ -@@ -295,7 +305,7 @@ exports.parserRules = { - } - } - } -- } -+ }*/ - token._tokenTrees[0] = []; - - return false; -diff --git a/node_modules/bnf/Script.js b/node_modules/bnf/Script.js -index e5eb5b7..56bd641 100755 ---- a/node_modules/bnf/Script.js -+++ b/node_modules/bnf/Script.js -@@ -15,7 +15,7 @@ exports.Script = class Script{ - } - - GetString( length, token ){ -- let str = this.rawScript.substring( token.point, token.point + length ); -+ let str = Buffer.from(this.rawScript).subarray( token.point, token.point + length ).toString('utf8'); - token.Seek( length ); - return str; - } -diff --git a/node_modules/bnf/Token.js b/node_modules/bnf/Token.js -index f592ae5..c76e103 100755 ---- a/node_modules/bnf/Token.js -+++ b/node_modules/bnf/Token.js -@@ -226,16 +226,27 @@ exports.Token = class Token{ - - TryString( charBuffer ){ - let stringBuffer = Buffer.alloc( charBuffer.length ); -- this.script.scriptBuffer.copy( stringBuffer, 0, this.point, charBuffer.length ); -+ this.script.scriptBuffer.copy( stringBuffer, 0, this.point, this.point + charBuffer.length ); - if( !stringBuffer.equals( charBuffer ) ){ - return false; - } - - this.SetValue( stringBuffer.toString() ); -- this.point += charArray.length; -+ this.point += charBuffer.length; - return true; - } - -+ TryRe(pattern) { -+ const mtch = this.script.scriptBuffer.subarray(this.point).toString('utf-8').match(pattern); -+ if (mtch && mtch[0]) { -+ const b = Buffer.from(mtch[0]); -+ this.SetValue(mtch[0]); -+ this.point+=b.length; -+ return true; -+ } -+ return false; -+ } -+ - SetChar( char ){ - this.SetValue( this.GetChar() ); - this.point++; diff --git a/patterns/patterns_bin/src/pattern_reg.rs b/patterns/patterns_bin/src/pattern_reg.rs deleted file mode 100644 index f8657961..00000000 --- a/patterns/patterns_bin/src/pattern_reg.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::pattern::Pattern; -use uuid::Uuid; - -pub struct PatternRegistry { - patterns: Vec<Pattern>, -} - -impl PatternRegistry { - pub const fn new() -> PatternRegistry { - PatternRegistry { patterns: Vec::new() } - } - - pub fn find_pattern(&mut self, str_text: &Vec<String>, i_text: &Vec<u64>, sample: String) -> &Pattern { - let mut idx: i32 = -1; - let mut mtc = 0; - for i in 0..self.patterns.len() { - mtc = self.patterns[i].match_text(&i_text); - if mtc == -1 || mtc > self.patterns[i].fluct { - continue; - } - idx = i as i32; - break; - } - - if idx == -1 { - let pattern = Pattern::new(Uuid::new_v4().to_string(), &i_text, &str_text, sample); - self.patterns.push(pattern); - idx = (self.patterns.len() - 1) as i32; - } else if mtc != 0 { - self.patterns[idx as usize].adjust_pattern(&i_text); - } - return &self.patterns[idx as usize]; - } - - pub fn to_string(&self) -> String { - let mut s = String::new(); - for i in 0..self.patterns.len() { - s += self.patterns[i].to_string().as_str(); - s += "\n"; - } - return s - } -} - -pub static mut REGISTRY: PatternRegistry = PatternRegistry::new(); \ No newline at end of file diff --git a/patterns/patterns_bin/src/tokens.rs b/patterns/patterns_bin/src/tokens.rs deleted file mode 100644 index 5d3f4449..00000000 --- a/patterns/patterns_bin/src/tokens.rs +++ /dev/null @@ -1,45 +0,0 @@ -use regex::{Regex, CaptureMatches, Match}; - -/*pub fn tokenize(re: &Regex, text: &str) -> CaptureMatches { - return re.captures_iter(text); -}*/ - -pub struct Tokenizer<'a> { - text: String, - pos: usize, - re: Regex, - iter: Option<CaptureMatches<'a, 'a>> -} - -impl Tokenizer<'_> { - pub fn new<'a>(text: &'a str) -> Tokenizer<'a> { - let mut res = Tokenizer { - text: text.to_string(), - pos: 0, - re: Regex::new(r"([\p{L}_]+|[\d.]+|[^\p{L}_\d.]+)\s*").unwrap(), - iter: None - }; - res - } -} - -impl Iterator for Tokenizer<'_> { - type Item = String; - - fn next(&mut self) -> Option<Self::Item> { - None - /*let cap: Option<Match> = None; - if let Some(c) = cap { - self.pos += c.get(0).unwrap().end(); - Some(c.get(0).unwrap().as_str().to_string()) - } else { - None - }*/ - } -} - -#[test] -fn test_tokenizer() { - let text = "Hello, world! 123"; - let mut tokenizer = Tokenizer::new(text); -} \ No newline at end of file diff --git a/plugins/README.md b/plugins/README.md deleted file mode 100644 index 134f582b..00000000 --- a/plugins/README.md +++ /dev/null @@ -1,279 +0,0 @@ -# Qryn Plugins - -* WORK IN PROGRESS! - -Missing a LogQL function in Qryn? Extend functionality in in _no time_ using [Qryn Plugins](https://github.com/metrico/qryn/tree/master/plugins) -Need to alias a complex query? Use macros to turn complex queries into easy to use queries - -## Overall plugin structure - -Plugins are supported via plugnplay module https://github.com/e0ipso/plugnplay . -To create a plugin you have to create a nodejs project with subfolders for each plugin or add them into your Qryn plugins folder: -``` -/ -|- package.json -|- plugin_name_folder -| |- plugnplay.yml -| |- index.js -|- plugin_2_folder - |- plugnplay.yml - ... -``` - -## Different types of plugins - -There is a number of different types of plugins supported by Qryn. Each type extends particular functionality: -- Log-range aggregator over unwrapped range: `unwrap_registry` type (vanilla LogQL example: avg_over_time) -- Custom macro function to wrap or shorten an existing request statement: `macros` type - - -## Plugin implementation - -### plugnplay.yml file -In order to initialize the plugin we need the `plugnplay.yml` file: - -``` -id: derivative -name: Derivative Plugin -description: Plugin to test pluggable extensions -loader: derivative.js -type: unwrap_registry -``` - -- `id` of the plugin should be unique. -- `type` of the plugin should be `unwrap_registry`. -- `loader` field should specify the js file exporting the plugin loader class. - -The js module specified in the `loader` field should export a class extending `PluginLoaderBase` class from the -plugnplay package. - -``` -const {PluginLoaderBase} = require('plugnplay'); -module.exports = `class extends PluginLoaderBase { - exportSync() { return {...}; } -} -``` - -The exporting class should implement one function: `exportSync() {...}`. -The `exportSync` function should return an object representing API different for each type of plugin. - -Finally, you have to add the path to your plugin root folder to the env variable `PLUGINS_PATH`. -Different paths should be separated by comma sign `,`. - -## Unwrapped Range Aggregation (unwrap_registry) - -In this example we will add a new unwrapped range aggregator `derivative`: - -`derivative=(last_unwrapped_value_in_range - first_unwrapped_value_in_range) / (last_time_in_range - first_time_in_range)` - -You need to init a plugin with the following loader: -``` -const {PluginLoaderBase} = require('plugnplay'); -module.exports = `class extends PluginLoaderBase { - exportSync(api) { - return { - derivative = { - run: () => {}, - approx: () => {} - } - } - } -} -``` -`exportSync` is a function returning an object with the function name as key and two methods: `run` and `approx`. - -The `run` method is called every time new unwrapped value accepted by the stream processor. Its declaration is: -``` - /** - * - * @param sum {any} previous value for the current time bucket - * @param val {{unwrapped: number}} current values - * @param time {number} timestamp in ms for the current value - * @returns {any} - */ - const run = (sum, val, time) => { - sum = sum || {}; - sum.first = sum && sum.first && time > sum.first.time ? sum.first : {time: time, val: val.unwrapped}; - sum.last = sum && sum.last && time < sum.last ? sum.last : {time: time, val: val.unwrapped}; - return sum; - } -``` - -So the run function accepts the previous aggregated value. The initial value is 0. -The second is an object with current unwrapped value. -And the time when the unwrapped value appeared in the database. -The run function should return the new sum. Data immutability is preferred but optional. - -The `approx` method is called for each bucket at the end of processing. Its declaration is: -``` - /** - * @param sum {any} sum of the time bucket you have created during "run" - * @returns {number} - */ - const approx = (sum) => { - return sum && sum.last && sum.first && sum.last.time > sum.first.time ? - (sum.last.val - sum.first.val) / (sum.last.time - sum.first.time) * 1000 : 0; - } -``` -The only argument is the result of the latest `run` call for the bucket. -The function should return number as result of the operator calculation for the provided time bucket. - -## Example - -The full code of the `derivative` plugin: - -plugnplay.yml -``` -id: derivative -name: Derivative Plugin -description: Plugin to test pluggable extensions -loader: derivative.js -type: unwrap_registry -``` - -derivative.js: -``` -const {PluginLoaderBase} = require('plugnplay'); - -module.exports = class extends PluginLoaderBase { - exportSync(api) { - return { - derivative: { - /** - * - * @param sum {any} previous value for the current time bucket - * @param val {{unwrapped: number}} current values - * @param time {number} timestamp in ms for the current value - * @returns {any} - */ - run: (sum, val, time) => { - sum = sum || {}; - sum.first = sum && sum.first && time > sum.first.time ? sum.first : { - time: time, - val: val.unwrapped - }; - sum.last = sum && sum.last && time < sum.last ? sum.last : {time: time, val: val.unwrapped}; - return sum; - }, - /** - * @param sum {any} sum of the time bucket you have created during "run" - * @returns {number} - */ - approx: (sum) => { - return sum && sum.last && sum.first && sum.last.time > sum.first.time ? - (sum.last.val - sum.first.val) / (sum.last.time - sum.first.time) * 1000 : 0; - } - } - }; - } -} -``` - -## Macro plugin implementation (macros) - -Qryn parses logql requests using the bnf package https://github.com/daKuleMune/nodebnf#readme - -You can provide a custom bnf token representation and map it to a relevant logql request via a plugin with `macros` -type. - -The raw ABNF description: https://github.com/metrico/qryn/blob/master/parser/logql.bnf . - -If you are unfamiliar BNF rules, here is a good resource to get a quick introduction: http://www.cs.umsl.edu/~janikow/cs4280/bnf.pdf - -### Custom BNF requirements - -A bnf description in your plugin should follow the requirements: -- one bnf rule on a string -- no multiline rules -- no comments supported -- bnf rule name should start with MACRO_ prefix -- no bnf rule name collisions - -### Plugin API -A plugin should export two fields: -``` -const exports = { - bnf: "... bnf rules ...", - /** - * - * @param token {Token} - * @returns {string} - */ - stringify: (token) => {} -} -``` -The `bnf` field should contain bnf rules. - -The `stringify` function should convert a parsed query token into a legit logQL request. - -### The `Token` type -Token type is a request parsed by the BNF package. It has the following fields: - -| Field | Header | Description | -| ----------- | ------------------------------------------- | ---------------- | -| value | token.value:string | part of the request expression corresponding to the token | -| Child | token.Child(child_type: string): Token | function returning the first token child with the specified type. | -| Children | token.Children(child_type: string): Token[] | function returning all the token children with the specified type. | - -### Example -Let's review an example of macro translating `test_macro("val1")` to `{test_id="val1"}` - -The `plugnplay.yml` file -``` -id: test_macro -name: test macro -description: A macro to test -loader: index.js -type: macros -``` - -The BNF description of the macro: `MACRO_test_macro_fn ::= "test_macro" <OWSP> "(" <OWSP> <quoted_str> <OWSP> ")"` - -The complete loader code: -``` -const {PluginLoaderBase} = require('plugnplay'); -module.exports = class extends PluginLoaderBase { - exportSync() { - return { - bnf: `MACRO_test_macro_fn ::= "test_macro" <OWSP> "(" <OWSP> <quoted_str> <OWSP> ")"`, - /** - * - * @param token {Token} - * @returns {string} - */ - stringify: (token) => { - return `{test_id=${token.Child('quoted_str').value}}`; - } - }; - } -} -``` - -### Commonly used tokens defined by the core BNF - -You can use the common rules already defined in the core BNF description. - -The raw ABNF description with all the rules: https://github.com/metrico/qryn/blob/master/parser/logql.bnf . - -The rules defined in the BNF package are here: https://github.com/daKuleMune/nodebnf#readme - -Commonly used LogQL rules: - -| Rule name | Example | Description | -| ------------------- | ------- | ----------- | -| log_stream_selector | <code>{label1 = "val1", l2 =~ "v2"} |~ "re1"</code> | log stream selector with label selectors and all pipeline operators -| log_stream_selector_rule | `label1 = "val1"` | one label selector rule -| label | `label1` | label name -| operator | `= / != / =~ / !~` | label selector operator -| quoted_str | `"qstr\""` | one properly quoted string -| line_filter_expression | <code>|~ "re1"</code> | one line filter expression -| line_filter_operator | <code>|= / |= / !~ / != </code> | string filter operator -| parser_expression | <code>| json jlbl="l1[1].l2" </code> | one parser expression -| label_filter_expression | <code>| jlbl = "val1" </code> | one label filter in the pipeline part -| line_format_expression | <code>| line_format "l1: {{label1}}" </code> | line format expression -| labels_format_expression | <code>| line_format lbl1="l1: {{label1}}" </code> | label format expression -| log_range_aggregation | `rate({label1="val1"} [1m])` | log range aggregation expression -| aggregation_operator | `sum(rate({label1="val1"} [1m])) by (lbl1, lbl2)` | aggregation operator expression -| unwrap_expression | <code>{label1="val1"} |~ "re1" | unwrap lbl2 </code> | line selector with pipeline ending with the unwrap expression -| unwrap_function | <code>rate(rate({label1="val1"} | unwrap int_lbl2 [1m]) by (label3)</code> | unwrapped log-range aggregation -| compared_agg_statement | <code>rate(rate({label1="val1"} | unwrap int_lbl2 [1m]) by (label3) > 5</code> | wrapped or unwrapped log-range aggregation comparef to a numeric const diff --git a/plugins/base/base.js b/plugins/base/base.js deleted file mode 100644 index 3fc09ee2..00000000 --- a/plugins/base/base.js +++ /dev/null @@ -1,12 +0,0 @@ -const { PluginLoaderBase } = require('plugnplay') -module.exports = class extends PluginLoaderBase { - exportSync () { - const res = { - validate: (plg) => { - res.props = Object.keys(plg) - return res.props - } - } - return res - } -} diff --git a/plugins/base/custom_processor/index.js b/plugins/base/custom_processor/index.js deleted file mode 100644 index 5cecd612..00000000 --- a/plugins/base/custom_processor/index.js +++ /dev/null @@ -1,11 +0,0 @@ -const { PluginTypeLoaderBase } = require('plugnplay') -module.exports = class extends PluginTypeLoaderBase { - exportSync (opts) { - return { - props: ['check', 'process'], - validate: (exports) => { - return exports - } - } - } -} diff --git a/plugins/base/custom_processor/plugnplay.yml b/plugins/base/custom_processor/plugnplay.yml deleted file mode 100644 index 3aa9ea0c..00000000 --- a/plugins/base/custom_processor/plugnplay.yml +++ /dev/null @@ -1,4 +0,0 @@ -id: custom_processor -name: Custom Processor Plugin -description: plugin to custom process a logql / promql request -loader: index.js \ No newline at end of file diff --git a/plugins/base/macros/index.js b/plugins/base/macros/index.js deleted file mode 100644 index 0d958b32..00000000 --- a/plugins/base/macros/index.js +++ /dev/null @@ -1,37 +0,0 @@ -const { PluginTypeLoaderBase } = require('plugnplay') -const ruleNames = new Set() -module.exports = class extends PluginTypeLoaderBase { - exportSync (opts) { - return { - props: ['bnf', 'stringify', '_main_rule_name'], - validate: (exports) => { - for (const f of ['bnf', 'stringify']) { - if (!exports[f]) { - throw new Error(`missing field ${f}`) - } - } - const rules = exports.bnf.split('\n') - if (rules[0] === '') { - throw new Error('First line should be the main rule') - } - for (const rule of rules) { - if (rule === '') { - continue - } - const name = rule.match(/^(\w+)\s*::=/) - if (!name) { - throw new Error(`invalid bnf rule: ${rule}`) - } - if (name[1].substr(0, 6) !== 'MACRO_') { - throw new Error(`${name[1]} token name should start with "MACRO_"`) - } - if (ruleNames.has(name[1])) { - throw new Error(`${name[1]} token already registered`) - } - ruleNames.add(name[1]) - } - exports._main_rule_name = rules[0].match(/^(\w+)\s*::=/)[1] - } - } - } -} diff --git a/plugins/base/macros/plugnplay.yml b/plugins/base/macros/plugnplay.yml deleted file mode 100644 index dc07e044..00000000 --- a/plugins/base/macros/plugnplay.yml +++ /dev/null @@ -1,4 +0,0 @@ -id: macros -name: Macros plugin base -description: Base plugin for macros type -loader: index.js \ No newline at end of file diff --git a/plugins/base/parser_registry/index.js b/plugins/base/parser_registry/index.js deleted file mode 100644 index a597c301..00000000 --- a/plugins/base/parser_registry/index.js +++ /dev/null @@ -1,12 +0,0 @@ -const Base = require('../base') -module.exports = class extends Base { - exportSync () { - const res = { - validate: (plg) => { - res.props = Object.entries(plg).filter(e => e[1].map || e[1].remap).map(e => e[0]) - return res.props - } - } - return res - } -} diff --git a/plugins/base/parser_registry/plugnplay.yml b/plugins/base/parser_registry/plugnplay.yml deleted file mode 100644 index 177f6f68..00000000 --- a/plugins/base/parser_registry/plugnplay.yml +++ /dev/null @@ -1,4 +0,0 @@ -id: parser_registry -name: Parser Base Plugin -description: This is the parser plugin type base class -loader: index.js \ No newline at end of file diff --git a/plugins/base/unwrap_registry/index.js b/plugins/base/unwrap_registry/index.js deleted file mode 100644 index 6e684ac0..00000000 --- a/plugins/base/unwrap_registry/index.js +++ /dev/null @@ -1,12 +0,0 @@ -const Base = require('../base') -module.exports = class extends Base { - exportSync () { - const res = { - validate: (plg) => { - res.props = Object.entries(plg).filter(e => e[1].run && e[1].approx).map(e => e[0]) - return res.props - } - } - return res - } -} diff --git a/plugins/base/unwrap_registry/plugnplay.yml b/plugins/base/unwrap_registry/plugnplay.yml deleted file mode 100644 index d2c5c6ce..00000000 --- a/plugins/base/unwrap_registry/plugnplay.yml +++ /dev/null @@ -1,4 +0,0 @@ -id: unwrap_registry -name: First Plugin -description: This is the first plugin to be tested. -loader: index.js \ No newline at end of file diff --git a/plugins/engine.js b/plugins/engine.js deleted file mode 100644 index 74d3f02d..00000000 --- a/plugins/engine.js +++ /dev/null @@ -1,38 +0,0 @@ -const { PluginManager } = require('plugnplay') - -const rootPath = !process.env.PLUGINS_PATH - ? __dirname - : `{${__dirname},${process.env.PLUGINS_PATH}}` - -const manager = new PluginManager({ - discovery: { - rootPath: rootPath, - allowsContributed: false - } -}) - -const plugins = manager.discoverSync() - -for (const plg of plugins) { - manager.require(plg.id) -} - -/** - * - * @param options {{id: string | undefined, type: string | undefined}} - * @returns {{}|*|null} - */ -module.exports.getPlg = (options) => { - if (options.id) { - return [...plugins.values()].some(p => p.id === options.id) ? manager.require(options.id).exports : null - } - if (options.type) { - const res = {} - for (const p of plugins) { - if (p.type === options.type) { - res[p.id] = manager.require(p.id).exports - } - } - return res - } -} diff --git a/plugins/extract_var/index.js b/plugins/extract_var/index.js deleted file mode 100644 index 004b7f25..00000000 --- a/plugins/extract_var/index.js +++ /dev/null @@ -1,16 +0,0 @@ -const { PluginLoaderBase } = require('plugnplay') -module.exports = class extends PluginLoaderBase { - exportSync () { - return { - bnf: 'MACRO_extract_var_fn ::= "extract" <OWSP> "(" <OWSP> <label> <OWSP> "," <OWSP> <label> <OWSP> "," <OWSP> <label> <OWSP> "," <OWSP> <label> <OWSP> ")"', - /** - * - * @param token {Token} - * @returns {string} - */ - stringify: (token) => { - return `first_over_time({${token.Children('label')[0].value}="${token.Children('label')[1].value}"} | json ${token.Children('label')[2].value}="${token.Children('label')[3].value}" | unwrap ${token.Children('label')[2].value} [5s]) by (http)` - } - } - } -} diff --git a/plugins/extract_var/plugnplay.yml b/plugins/extract_var/plugnplay.yml deleted file mode 100644 index 65d1426f..00000000 --- a/plugins/extract_var/plugnplay.yml +++ /dev/null @@ -1,5 +0,0 @@ -id: export_var -name: export var -description: Export one variable from log stream -loader: index.js -type: macros diff --git a/plugins/label_to_row/index.js b/plugins/label_to_row/index.js deleted file mode 100644 index e5ee02d7..00000000 --- a/plugins/label_to_row/index.js +++ /dev/null @@ -1,41 +0,0 @@ -const { PluginLoaderBase } = require('plugnplay') - -module.exports = class extends PluginLoaderBase { - exportSync (options) { - return { - label_to_row: { - /** - * - * @param parameters {string[]} - */ - remap: (parameters) => { - const labelsToRemap = parameters.length - ? JSON.parse(parameters[0]).split(',').map(p => p.trim()) - : undefined - return (emit, entry) => { - if (labelsToRemap) { - for (const l of labelsToRemap) { - if (entry.labels[l]) { - const rm = { - ...entry, - labels: { label: l }, - string: entry.labels[l] - } - emit(rm) - } - } - return - } - for (const [l, v] of Object.entries(entry)) { - emit({ - ...entry, - labels: { label: l }, - string: v - }) - } - } - } - } - } - } -} diff --git a/plugins/label_to_row/plugnplay.yml b/plugins/label_to_row/plugnplay.yml deleted file mode 100644 index c7de3213..00000000 --- a/plugins/label_to_row/plugnplay.yml +++ /dev/null @@ -1,5 +0,0 @@ -id: label_to_row -name: Label to Time Series -description: Convert label to extra time series -loader: index.js -type: parser_registry \ No newline at end of file diff --git a/plugins/least_over_time/least.js b/plugins/least_over_time/least.js deleted file mode 100644 index 5d0ae7ff..00000000 --- a/plugins/least_over_time/least.js +++ /dev/null @@ -1,30 +0,0 @@ -const { PluginLoaderBase } = require('plugnplay') - -module.exports = class extends PluginLoaderBase { - exportSync (api) { - return { - least_over_time: { - /** - * - * @param lowest {any} previous value for the current time bucket - * @param val {{unwrapped: number}} current values - * @param time {number} timestamp in ms for the current value - * @returns {any} - */ - run: (lowest, val, time) => { - if (lowest === 0 || val.unwrapped < lowest) { - lowest = val.unwrapped - } - return lowest - }, - /** - * @param lowest {any} lowest of the time bucket you have created during "run" - * @returns {number} - */ - approx: (lowest) => { - return lowest - } - } - } - } -} diff --git a/plugins/least_over_time/plugnplay.yml b/plugins/least_over_time/plugnplay.yml deleted file mode 100644 index 9fa96c7c..00000000 --- a/plugins/least_over_time/plugnplay.yml +++ /dev/null @@ -1,5 +0,0 @@ -id: leastovertime -name: Least Value Over Time Plugin -description: Plugin to return least number over time frame -loader: least.js -type: unwrap_registry diff --git a/plugins/output_format/index.js b/plugins/output_format/index.js deleted file mode 100644 index aed06089..00000000 --- a/plugins/output_format/index.js +++ /dev/null @@ -1,54 +0,0 @@ -const { PluginLoaderBase } = require('plugnplay') - -/** - * @class Plugin - * @property {string} query - * @property start {number} start in NS - * @property end {string} end in NS - * @property type {string} promql or logql - * @property limit {number} - * @property {{ - * logql: (query: string, startNS: number, endNS: number, limit: number) => Promise<Object> - * }} API - * promql: (query: string, startNS: number, endNS: number, limit: number) => Promise<Object> //not implemented - */ -class Plugin { - /** - * @method - * @name check - * @this {Plg} - * @returns {boolean} if this plugin is usable for the query - */ - check () { - return this.query.match(/^toCsv\(.+\)\s*$/) - } - - /** - * @method - * @name process - * @this {Plg} - * @returns {Promise<{type: string, out: string}>} The raw output - */ - async process () { - const match = this.query.match(/^toCsv\((.+)\)$/) - const response = await this.API.logql(match[1], this.start, this.end, this.limit) - let res = '' - for (const stream of response.data.result) { - const labels = JSON.stringify(stream.stream) - for (const val of stream.values) { - res += `${labels}\t${val[0]}\t${val[1]}\n` - } - } - return { - type: 'text/csv', - out: res - } - } -} -class Plg extends PluginLoaderBase { - exportSync (api) { - return new Plugin() - } -} - -module.exports = Plg diff --git a/plugins/output_format/plugnplay.yml b/plugins/output_format/plugnplay.yml deleted file mode 100644 index ee95df18..00000000 --- a/plugins/output_format/plugnplay.yml +++ /dev/null @@ -1,5 +0,0 @@ -id: output_format -name: Format Output -description: Change output format -loader: index.js -type: custom_processor \ No newline at end of file diff --git a/plugins/test_macro/index.js b/plugins/test_macro/index.js deleted file mode 100644 index d34995e4..00000000 --- a/plugins/test_macro/index.js +++ /dev/null @@ -1,16 +0,0 @@ -const { PluginLoaderBase } = require('plugnplay') -module.exports = class extends PluginLoaderBase { - exportSync () { - return { - bnf: 'MACRO_test_macro_fn ::= "test_macro" <OWSP> "(" <OWSP> <quoted_str> <OWSP> ")"', - /** - * - * @param token {Token} - * @returns {string} - */ - stringify: (token) => { - return `{test_id=${token.Child('quoted_str').value}}` - } - } - } -} diff --git a/plugins/test_macro/plugnplay.yml b/plugins/test_macro/plugnplay.yml deleted file mode 100644 index 41b2d082..00000000 --- a/plugins/test_macro/plugnplay.yml +++ /dev/null @@ -1,5 +0,0 @@ -id: test_macro -name: test macro -description: A macro to test -loader: index.js -type: macros \ No newline at end of file diff --git a/plugins/unwrap_registry/derivative.js b/plugins/unwrap_registry/derivative.js deleted file mode 100644 index 9bd55396..00000000 --- a/plugins/unwrap_registry/derivative.js +++ /dev/null @@ -1,37 +0,0 @@ -const { PluginLoaderBase } = require('plugnplay') - -module.exports = class extends PluginLoaderBase { - exportSync (api) { - return { - derivative: { - /** - * - * @param sum {any} previous value for the current time bucket - * @param val {{unwrapped: number}} current values - * @param time {number} timestamp in ms for the current value - * @returns {any} - */ - run: (sum, val, time) => { - sum = sum || {} - sum.first = sum && sum.first && time > sum.first.time - ? sum.first - : { - time: time, - val: val.unwrapped - } - sum.last = sum && sum.last && time < sum.last ? sum.last : { time: time, val: val.unwrapped } - return sum - }, - /** - * @param sum {any} sum of the time bucket you have created during "run" - * @returns {number} - */ - approx: (sum) => { - return sum && sum.last && sum.first && sum.last.time > sum.first.time - ? (sum.last.val - sum.first.val) / (sum.last.time - sum.first.time) * 1000 - : 0 - } - } - } - } -} diff --git a/plugins/unwrap_registry/plugnplay.yml b/plugins/unwrap_registry/plugnplay.yml deleted file mode 100644 index 0c5242a8..00000000 --- a/plugins/unwrap_registry/plugnplay.yml +++ /dev/null @@ -1,5 +0,0 @@ -id: derivative -name: Derivative Plugin -description: Plugin to test pluggable extensions -loader: derivative.js -type: unwrap_registry \ No newline at end of file diff --git a/pm2.ecosystem.js b/pm2.ecosystem.js deleted file mode 100644 index 135bf447..00000000 --- a/pm2.ecosystem.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = { - apps: [{ - name: 'qryn', - script: './qryn.mjs', - env: { - CLICKHOUSE_SERVER: 'localhost', - CLICKHOUSE_PORT: 8123, - CLICKHOUSE_AUTH: 'default:password', - CLICKHOUSE_DB: 'cloki', - TIMEFIELD: 'record_datetime', - LABELS_DAYS: 7, - SAMPLES_DAYS: 7, - DEBUG: false - } - }] -} diff --git a/promql/index.js b/promql/index.js deleted file mode 100644 index a0090ceb..00000000 --- a/promql/index.js +++ /dev/null @@ -1,233 +0,0 @@ -const Sql = require('@cloki/clickhouse-sql') -const prometheus = require('../wasm_parts/main') -const { rawRequest } = require('../lib/db/clickhouse') -const { DATABASE_NAME } = require('../lib/utils') -const { clusterName, metricType, bothType } = require('../common') -const _dist = clusterName ? '_dist' : '' - -class PSQLError extends Error {} -module.exports.PSQLError = PSQLError - -/** - * - * @param query {string} - * @param startMs {number} - * @param endMs {number} - * @param stepMs {number} - */ -module.exports.rangeQuery = async (query, startMs, endMs, stepMs) => { - let resp - try { - resp = await prometheus.pqlRangeQuery(query, startMs, endMs, stepMs, module.exports.getData) - return JSON.parse(resp) - } catch (e) { - if (e instanceof prometheus.WasmError) { - throw new PSQLError(e.message) - } - throw e - } -} - -module.exports.instantQuery = async (query, timeMs) => { - try { - const resp = await prometheus.pqlInstantQuery(query, timeMs, module.exports.getData) - return JSON.parse(resp) - } catch (e) { - if (e instanceof prometheus.WasmError) { - throw new PSQLError(e.message) - } - throw e - } -} - -module.exports.series = async (query, fromMs, toMs) => { - try { - const fromS = Math.floor(fromMs / 1000) - const toS = Math.floor(toMs / 1000) - const matchers = prometheus.pqlMatchers(query) - const idx = getIdxSubqueryV2(matchers[0], fromMs, toMs) - const withIdx = new Sql.With('idx', idx, !!clusterName) - const req = (new Sql.Select()) - .with(withIdx) - .select([new Sql.Raw('any(labels)'), 'labels']) - .from(`time_series${_dist}`) - .where(Sql.And( - Sql.Gte('date', new Sql.Raw(`toDate(fromUnixTimestamp(${fromS}))`)), - Sql.Lte('date', new Sql.Raw(`toDate(fromUnixTimestamp(${toS}))`)), - new Sql.In('fingerprint', 'in', new Sql.WithReference(withIdx)), - new Sql.In('type', 'in', [bothType,metricType]))) - .groupBy(new Sql.Raw('fingerprint')) - const data = await rawRequest(req.toString() + ' FORMAT JSON', - null, - DATABASE_NAME()) - return data.data.data.map(l => - Object.fromEntries(Object.entries(JSON.parse(l.labels)).filter(e => e[1])) - ) - } catch (e) { - if (e instanceof prometheus.WasmError) { - throw new PSQLError(e.message) - } - throw e - } -} - -/** - * - * @param matcher {[string]} - */ -const getMatcherIdxCond = (matcher) => { - const res = [ - Sql.Eq('key', matcher[0]) - ] - switch (matcher[1]) { - case '=': - res.push(Sql.Eq('val', matcher[2])) - break - case '!=': - res.push(Sql.Ne('val', matcher[2])) - break - case '=~': - res.push(Sql.Eq(new Sql.Raw(`match(val, ${Sql.quoteVal(matcher[2])})`), 1)) - break - case '!~': - res.push(Sql.Ne(new Sql.Raw(`match(val, ${Sql.quoteVal(matcher[2])})`), 1)) - } - return res -} - -/** - * - * @param matchers {[[string]]} - */ -const getMatchersIdxCond = (matchers) => { - return matchers.map(matcher => Sql.And(...getMatcherIdxCond(matcher))) -} - -const getIdxSubqueryV2 = (matchers, fromMs, toMs) => { - const fromS = Math.floor(fromMs / 1000) - const toS = Math.floor(toMs / 1000) - const nonEmptyMatchers = matchers.filter(m => m[2] !== '') - const emptyMatchers = matchers.filter(m => m[2] === '' && ['=', '!='].includes(m[1])) - let req = null - if (nonEmptyMatchers.length) { - const nonEmptyConds = getMatchersIdxCond(nonEmptyMatchers) - req = (new Sql.Select()) - .select('fingerprint') - .from([DATABASE_NAME() + '.time_series_gin', 'time_series_gin']) - .where(Sql.And( - Sql.Or(...nonEmptyConds), - Sql.Gte('date', new Sql.Raw(`toDate(fromUnixTimestamp(${fromS}))`)), - Sql.Lte('date', new Sql.Raw(`toDate(fromUnixTimestamp(${toS}))`)), - new Sql.In('type', 'in', [bothType, metricType]))) - .having( - Sql.Eq( - new Sql.Raw('groupBitOr(' + nonEmptyConds.map( - (m, i) => new Sql.Raw(`bitShiftLeft((${m})::UInt64, ${i})`) - ).join('+') + ')'), (1 << nonEmptyConds.length) - 1) - ).groupBy('fingerprint') - } - if (emptyMatchers.length) { - const emptyConds = emptyMatchers.map(m => { - const visitParamHas = new Sql.Raw('') - visitParamHas.toString = function () { - return `visitParamHas(labels, ${Sql.quoteVal(m[0])})` - } - switch (m[1]) { - case '=': - return Sql.Eq(visitParamHas, new Sql.Raw('0')) - case '!=': - return Sql.Ne(visitParamHas, new Sql.Raw('1')) - default: - return null - } - }).filter(m => !!m) - const emptyReq = (new Sql.Select()) - .select('fingerprint') - .from(`time_series${_dist}`) - .where(Sql.And(...emptyConds)) - if (nonEmptyMatchers.length) { - const withNonEmptyIdx = new Sql.With('nonEmptyIdx', req, !!clusterName) - emptyReq.with(withNonEmptyIdx) - .where( - new Sql.In('fingerprint', 'in', new Sql.WithReference(withNonEmptyIdx)) - ) - } - req = emptyReq - } - return req -} - -module.exports.getData = async (matchers, fromMs, toMs, subqueries) => { - const db = DATABASE_NAME() - const subq = (subqueries || {})[getMetricName(matchers)] - if (subq) { - console.log(subq) - const data = await rawRequest(subq + ' FORMAT RowBinary', - null, db, { responseType: 'arraybuffer' }) - return new Uint8Array(data.data) - } - const idx = getIdxSubqueryV2(matchers, fromMs, toMs) - const withIdx = new Sql.With('idx', idx, !!clusterName) - const timeSeries = (new Sql.Select()) - .select( - 'fingerprint', - [new Sql.Raw('arraySort(JSONExtractKeysAndValues(labels, \'String\'))'), 'labels'] - ).from(DATABASE_NAME() + '.time_series') - .where(Sql.And( - new Sql.In('fingerprint', 'in', new Sql.WithReference(withIdx)), - new Sql.In('type', 'in', [bothType, metricType]))) - const withTimeSeries = new Sql.With('timeSeries', timeSeries, !!clusterName) - const raw = (new Sql.Select()) - .with(withIdx) - .select( - [new Sql.Raw('argMaxMerge(last)'), 'value'], - 'fingerprint', - [new Sql.Raw('intDiv(timestamp_ns, 15000000000) * 15000'), 'timestamp_ms']) - .from([`metrics_15s${_dist}`, 'metrics_15s']) - .where( - new Sql.And( - new Sql.In('fingerprint', 'in', new Sql.WithReference(withIdx)), - Sql.Gte('timestamp_ns', new Sql.Raw(`${fromMs}000000`)), - Sql.Lte('timestamp_ns', new Sql.Raw(`${toMs}000000`)), - new Sql.In('type', 'in', [bothType, metricType])) - ).groupBy('fingerprint', 'timestamp_ms') - .orderBy('fingerprint', 'timestamp_ms') - if (clusterName) { - raw.select([new Sql.Raw('min(time_series.labels)'), 'labels']).join( - [new Sql.WithReference(withTimeSeries), 'time_series'], - 'any left', - Sql.Eq('time_series.fingerprint', new Sql.Raw('metrics_15s.fingerprint')) - ) - } - const withRaw = new Sql.With('raw', raw, !!clusterName) - const res = (new Sql.Select()) - .with(withRaw) - .select( - [new Sql.Raw('any(labels)'), 'stream'], - [new Sql.Raw('arraySort(groupArray((raw.timestamp_ms, raw.value)))'), 'values'] - ).from([new Sql.WithReference(withRaw), 'raw']) - .groupBy('raw.fingerprint') - .orderBy('raw.fingerprint') - if (!clusterName) { - res.with(withTimeSeries) - .join( - [new Sql.WithReference(withTimeSeries), 'time_series'], - 'any left', - Sql.Eq('time_series.fingerprint', new Sql.Raw('raw.fingerprint')) - ) - } - console.log(res.toString()) - const data = await rawRequest(res.toString() + ' FORMAT RowBinary', - null, db, { responseType: 'arraybuffer' }) - return new Uint8Array(data.data) -} - -function getMetricName(matchers) { - for (const matcher of matchers) { - if (matcher[0] === '__name__' && matcher[1] === '=') { - return matcher[2] - } - } -} - -prometheus.getData = module.exports.getData diff --git a/pyro.go b/pyro.go new file mode 100644 index 00000000..b4ab6466 --- /dev/null +++ b/pyro.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + "github.com/grafana/pyroscope-go" + "log" + "os" +) + +func initPyro() { + // Pyroscope configuration + serverAddress := os.Getenv("PYROSCOPE_SERVER_ADDRESS") + if serverAddress == "" { + return + } + + applicationName := os.Getenv("PYROSCOPE_APPLICATION_NAME") + if applicationName == "" { + applicationName = "gigapipe" + } + + // Initialize Pyroscope + config := pyroscope.Config{ + ApplicationName: applicationName, + ServerAddress: serverAddress, + Logger: pyroscope.StandardLogger, + ProfileTypes: []pyroscope.ProfileType{ + pyroscope.ProfileCPU, + pyroscope.ProfileAllocObjects, + pyroscope.ProfileAllocSpace, + pyroscope.ProfileInuseObjects, + pyroscope.ProfileInuseSpace, + }, + } + + _, err := pyroscope.Start(config) + if err != nil { + log.Fatalf("Failed to start Pyroscope: %v", err) + } + fmt.Println("Pyroscope profiling started") +} diff --git a/pyroscope/flamebearer.d.ts b/pyroscope/flamebearer.d.ts deleted file mode 100644 index 7ec2f880..00000000 --- a/pyroscope/flamebearer.d.ts +++ /dev/null @@ -1,67 +0,0 @@ - -type int64 = string; -type uint64 = string; -type units = string; - -export interface Flamebearer { - version: number, - flamebearerProfileV1: flamebearerProfileV1 - telemetry?: {[key: string]: any} -} - -export interface flamebearerProfileV1 { - flamebearer: flamebearerV1, - metadata: flamebearerMetadataV1, - timeline: flamebearerTimelineV1, - groups: {[key: string]: flamebearerTimelineV1} - heatmap: heatmap, - leftTicks: string, - rightTicks: string, -} - -export interface flamebearerV1 { - names: string, - levels: [[number]], - numTicks: number, - maxSelf: number -} - -export interface flamebearerMetadataV1 { - format: string, - spyName: string, - sampleRate: number, - units: units, - name: string -} - -export interface flamebearerTimelineV1 { - startTime: int64, - samples: [uint64] - durationDelta: int64, - watermarks: {[key: number]: int64} -} - -export interface heatmap { - values: [[uint64]], - timeBuckets: int64, - valueBuckets: int64, - startTime: int64, - endTime: int64, - minValue: uint64, - maxValue: uint64, - minDepth: uint64, - maxDepth: uint64 -} - -export interface level { - values: number[] -} - -export interface flamegraphDiff { - name: string[], - levels: level[], - total: int64, - maxSelf: int64, - leftTicks: int64, - rightTicks: int64 -} diff --git a/pyroscope/google/v1/profile_grpc_pb.js b/pyroscope/google/v1/profile_grpc_pb.js deleted file mode 100644 index 97b3a246..00000000 --- a/pyroscope/google/v1/profile_grpc_pb.js +++ /dev/null @@ -1 +0,0 @@ -// GENERATED CODE -- NO SERVICES IN PROTO \ No newline at end of file diff --git a/pyroscope/google/v1/profile_pb.js b/pyroscope/google/v1/profile_pb.js deleted file mode 100644 index 3eab57a6..00000000 --- a/pyroscope/google/v1/profile_pb.js +++ /dev/null @@ -1,2635 +0,0 @@ -// source: google/v1/profile.proto -/** - * @fileoverview - * @enhanceable - * @suppress {missingRequire} reports error on implicit type usages. - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! -/* eslint-disable */ -// @ts-nocheck - -var jspb = require('google-protobuf'); -var goog = jspb; -var global = (function() { - if (this) { return this; } - if (typeof window !== 'undefined') { return window; } - if (typeof global !== 'undefined') { return global; } - if (typeof self !== 'undefined') { return self; } - return Function('return this')(); -}.call(null)); - -goog.exportSymbol('proto.google.v1.Function', null, global); -goog.exportSymbol('proto.google.v1.Label', null, global); -goog.exportSymbol('proto.google.v1.Line', null, global); -goog.exportSymbol('proto.google.v1.Location', null, global); -goog.exportSymbol('proto.google.v1.Mapping', null, global); -goog.exportSymbol('proto.google.v1.Profile', null, global); -goog.exportSymbol('proto.google.v1.Sample', null, global); -goog.exportSymbol('proto.google.v1.ValueType', null, global); -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.google.v1.Profile = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.google.v1.Profile.repeatedFields_, null); -}; -goog.inherits(proto.google.v1.Profile, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.google.v1.Profile.displayName = 'proto.google.v1.Profile'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.google.v1.ValueType = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.google.v1.ValueType, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.google.v1.ValueType.displayName = 'proto.google.v1.ValueType'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.google.v1.Sample = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.google.v1.Sample.repeatedFields_, null); -}; -goog.inherits(proto.google.v1.Sample, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.google.v1.Sample.displayName = 'proto.google.v1.Sample'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.google.v1.Label = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.google.v1.Label, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.google.v1.Label.displayName = 'proto.google.v1.Label'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.google.v1.Mapping = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.google.v1.Mapping, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.google.v1.Mapping.displayName = 'proto.google.v1.Mapping'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.google.v1.Location = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.google.v1.Location.repeatedFields_, null); -}; -goog.inherits(proto.google.v1.Location, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.google.v1.Location.displayName = 'proto.google.v1.Location'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.google.v1.Line = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.google.v1.Line, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.google.v1.Line.displayName = 'proto.google.v1.Line'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.google.v1.Function = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.google.v1.Function, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.google.v1.Function.displayName = 'proto.google.v1.Function'; -} - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.google.v1.Profile.repeatedFields_ = [1,2,3,4,5,6,13]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.google.v1.Profile.prototype.toObject = function(opt_includeInstance) { - return proto.google.v1.Profile.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.google.v1.Profile} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.google.v1.Profile.toObject = function(includeInstance, msg) { - var f, obj = { - sampleTypeList: jspb.Message.toObjectList(msg.getSampleTypeList(), - proto.google.v1.ValueType.toObject, includeInstance), - sampleList: jspb.Message.toObjectList(msg.getSampleList(), - proto.google.v1.Sample.toObject, includeInstance), - mappingList: jspb.Message.toObjectList(msg.getMappingList(), - proto.google.v1.Mapping.toObject, includeInstance), - locationList: jspb.Message.toObjectList(msg.getLocationList(), - proto.google.v1.Location.toObject, includeInstance), - functionList: jspb.Message.toObjectList(msg.getFunctionList(), - proto.google.v1.Function.toObject, includeInstance), - stringTableList: (f = jspb.Message.getRepeatedField(msg, 6)) == null ? undefined : f, - dropFrames: jspb.Message.getFieldWithDefault(msg, 7, 0), - keepFrames: jspb.Message.getFieldWithDefault(msg, 8, 0), - timeNanos: jspb.Message.getFieldWithDefault(msg, 9, 0), - durationNanos: jspb.Message.getFieldWithDefault(msg, 10, 0), - periodType: (f = msg.getPeriodType()) && proto.google.v1.ValueType.toObject(includeInstance, f), - period: jspb.Message.getFieldWithDefault(msg, 12, 0), - commentList: (f = jspb.Message.getRepeatedField(msg, 13)) == null ? undefined : f, - defaultSampleType: jspb.Message.getFieldWithDefault(msg, 14, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.google.v1.Profile} - */ -proto.google.v1.Profile.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.google.v1.Profile; - return proto.google.v1.Profile.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.google.v1.Profile} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.google.v1.Profile} - */ -proto.google.v1.Profile.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = new proto.google.v1.ValueType; - reader.readMessage(value,proto.google.v1.ValueType.deserializeBinaryFromReader); - msg.addSampleType(value); - break; - case 2: - var value = new proto.google.v1.Sample; - reader.readMessage(value,proto.google.v1.Sample.deserializeBinaryFromReader); - msg.addSample(value); - break; - case 3: - var value = new proto.google.v1.Mapping; - reader.readMessage(value,proto.google.v1.Mapping.deserializeBinaryFromReader); - msg.addMapping(value); - break; - case 4: - var value = new proto.google.v1.Location; - reader.readMessage(value,proto.google.v1.Location.deserializeBinaryFromReader); - msg.addLocation(value); - break; - case 5: - var value = new proto.google.v1.Function; - reader.readMessage(value,proto.google.v1.Function.deserializeBinaryFromReader); - msg.addFunction(value); - break; - case 6: - var value = /** @type {string} */ (reader.readString()); - msg.addStringTable(value); - break; - case 7: - var value = /** @type {number} */ (reader.readInt64()); - msg.setDropFrames(value); - break; - case 8: - var value = /** @type {number} */ (reader.readInt64()); - msg.setKeepFrames(value); - break; - case 9: - var value = /** @type {number} */ (reader.readInt64()); - msg.setTimeNanos(value); - break; - case 10: - var value = /** @type {number} */ (reader.readInt64()); - msg.setDurationNanos(value); - break; - case 11: - var value = new proto.google.v1.ValueType; - reader.readMessage(value,proto.google.v1.ValueType.deserializeBinaryFromReader); - msg.setPeriodType(value); - break; - case 12: - var value = /** @type {number} */ (reader.readInt64()); - msg.setPeriod(value); - break; - case 13: - var values = /** @type {!Array<number>} */ (reader.isDelimited() ? reader.readPackedInt64() : [reader.readInt64()]); - for (var i = 0; i < values.length; i++) { - msg.addComment(values[i]); - } - break; - case 14: - var value = /** @type {number} */ (reader.readInt64()); - msg.setDefaultSampleType(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.google.v1.Profile.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.google.v1.Profile.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.google.v1.Profile} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.google.v1.Profile.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getSampleTypeList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 1, - f, - proto.google.v1.ValueType.serializeBinaryToWriter - ); - } - f = message.getSampleList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 2, - f, - proto.google.v1.Sample.serializeBinaryToWriter - ); - } - f = message.getMappingList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 3, - f, - proto.google.v1.Mapping.serializeBinaryToWriter - ); - } - f = message.getLocationList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 4, - f, - proto.google.v1.Location.serializeBinaryToWriter - ); - } - f = message.getFunctionList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 5, - f, - proto.google.v1.Function.serializeBinaryToWriter - ); - } - f = message.getStringTableList(); - if (f.length > 0) { - writer.writeRepeatedString( - 6, - f - ); - } - f = message.getDropFrames(); - if (f !== 0) { - writer.writeInt64( - 7, - f - ); - } - f = message.getKeepFrames(); - if (f !== 0) { - writer.writeInt64( - 8, - f - ); - } - f = message.getTimeNanos(); - if (f !== 0) { - writer.writeInt64( - 9, - f - ); - } - f = message.getDurationNanos(); - if (f !== 0) { - writer.writeInt64( - 10, - f - ); - } - f = message.getPeriodType(); - if (f != null) { - writer.writeMessage( - 11, - f, - proto.google.v1.ValueType.serializeBinaryToWriter - ); - } - f = message.getPeriod(); - if (f !== 0) { - writer.writeInt64( - 12, - f - ); - } - f = message.getCommentList(); - if (f.length > 0) { - writer.writePackedInt64( - 13, - f - ); - } - f = message.getDefaultSampleType(); - if (f !== 0) { - writer.writeInt64( - 14, - f - ); - } -}; - - -/** - * repeated ValueType sample_type = 1; - * @return {!Array<!proto.google.v1.ValueType>} - */ -proto.google.v1.Profile.prototype.getSampleTypeList = function() { - return /** @type{!Array<!proto.google.v1.ValueType>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.google.v1.ValueType, 1)); -}; - - -/** - * @param {!Array<!proto.google.v1.ValueType>} value - * @return {!proto.google.v1.Profile} returns this -*/ -proto.google.v1.Profile.prototype.setSampleTypeList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 1, value); -}; - - -/** - * @param {!proto.google.v1.ValueType=} opt_value - * @param {number=} opt_index - * @return {!proto.google.v1.ValueType} - */ -proto.google.v1.Profile.prototype.addSampleType = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.google.v1.ValueType, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.clearSampleTypeList = function() { - return this.setSampleTypeList([]); -}; - - -/** - * repeated Sample sample = 2; - * @return {!Array<!proto.google.v1.Sample>} - */ -proto.google.v1.Profile.prototype.getSampleList = function() { - return /** @type{!Array<!proto.google.v1.Sample>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.google.v1.Sample, 2)); -}; - - -/** - * @param {!Array<!proto.google.v1.Sample>} value - * @return {!proto.google.v1.Profile} returns this -*/ -proto.google.v1.Profile.prototype.setSampleList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 2, value); -}; - - -/** - * @param {!proto.google.v1.Sample=} opt_value - * @param {number=} opt_index - * @return {!proto.google.v1.Sample} - */ -proto.google.v1.Profile.prototype.addSample = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 2, opt_value, proto.google.v1.Sample, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.clearSampleList = function() { - return this.setSampleList([]); -}; - - -/** - * repeated Mapping mapping = 3; - * @return {!Array<!proto.google.v1.Mapping>} - */ -proto.google.v1.Profile.prototype.getMappingList = function() { - return /** @type{!Array<!proto.google.v1.Mapping>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.google.v1.Mapping, 3)); -}; - - -/** - * @param {!Array<!proto.google.v1.Mapping>} value - * @return {!proto.google.v1.Profile} returns this -*/ -proto.google.v1.Profile.prototype.setMappingList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 3, value); -}; - - -/** - * @param {!proto.google.v1.Mapping=} opt_value - * @param {number=} opt_index - * @return {!proto.google.v1.Mapping} - */ -proto.google.v1.Profile.prototype.addMapping = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 3, opt_value, proto.google.v1.Mapping, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.clearMappingList = function() { - return this.setMappingList([]); -}; - - -/** - * repeated Location location = 4; - * @return {!Array<!proto.google.v1.Location>} - */ -proto.google.v1.Profile.prototype.getLocationList = function() { - return /** @type{!Array<!proto.google.v1.Location>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.google.v1.Location, 4)); -}; - - -/** - * @param {!Array<!proto.google.v1.Location>} value - * @return {!proto.google.v1.Profile} returns this -*/ -proto.google.v1.Profile.prototype.setLocationList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 4, value); -}; - - -/** - * @param {!proto.google.v1.Location=} opt_value - * @param {number=} opt_index - * @return {!proto.google.v1.Location} - */ -proto.google.v1.Profile.prototype.addLocation = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 4, opt_value, proto.google.v1.Location, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.clearLocationList = function() { - return this.setLocationList([]); -}; - - -/** - * repeated Function function = 5; - * @return {!Array<!proto.google.v1.Function>} - */ -proto.google.v1.Profile.prototype.getFunctionList = function() { - return /** @type{!Array<!proto.google.v1.Function>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.google.v1.Function, 5)); -}; - - -/** - * @param {!Array<!proto.google.v1.Function>} value - * @return {!proto.google.v1.Profile} returns this -*/ -proto.google.v1.Profile.prototype.setFunctionList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 5, value); -}; - - -/** - * @param {!proto.google.v1.Function=} opt_value - * @param {number=} opt_index - * @return {!proto.google.v1.Function} - */ -proto.google.v1.Profile.prototype.addFunction = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 5, opt_value, proto.google.v1.Function, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.clearFunctionList = function() { - return this.setFunctionList([]); -}; - - -/** - * repeated string string_table = 6; - * @return {!Array<string>} - */ -proto.google.v1.Profile.prototype.getStringTableList = function() { - return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 6)); -}; - - -/** - * @param {!Array<string>} value - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.setStringTableList = function(value) { - return jspb.Message.setField(this, 6, value || []); -}; - - -/** - * @param {string} value - * @param {number=} opt_index - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.addStringTable = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 6, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.clearStringTableList = function() { - return this.setStringTableList([]); -}; - - -/** - * optional int64 drop_frames = 7; - * @return {number} - */ -proto.google.v1.Profile.prototype.getDropFrames = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.setDropFrames = function(value) { - return jspb.Message.setProto3IntField(this, 7, value); -}; - - -/** - * optional int64 keep_frames = 8; - * @return {number} - */ -proto.google.v1.Profile.prototype.getKeepFrames = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 8, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.setKeepFrames = function(value) { - return jspb.Message.setProto3IntField(this, 8, value); -}; - - -/** - * optional int64 time_nanos = 9; - * @return {number} - */ -proto.google.v1.Profile.prototype.getTimeNanos = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 9, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.setTimeNanos = function(value) { - return jspb.Message.setProto3IntField(this, 9, value); -}; - - -/** - * optional int64 duration_nanos = 10; - * @return {number} - */ -proto.google.v1.Profile.prototype.getDurationNanos = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 10, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.setDurationNanos = function(value) { - return jspb.Message.setProto3IntField(this, 10, value); -}; - - -/** - * optional ValueType period_type = 11; - * @return {?proto.google.v1.ValueType} - */ -proto.google.v1.Profile.prototype.getPeriodType = function() { - return /** @type{?proto.google.v1.ValueType} */ ( - jspb.Message.getWrapperField(this, proto.google.v1.ValueType, 11)); -}; - - -/** - * @param {?proto.google.v1.ValueType|undefined} value - * @return {!proto.google.v1.Profile} returns this -*/ -proto.google.v1.Profile.prototype.setPeriodType = function(value) { - return jspb.Message.setWrapperField(this, 11, value); -}; - - -/** - * Clears the message field making it undefined. - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.clearPeriodType = function() { - return this.setPeriodType(undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.google.v1.Profile.prototype.hasPeriodType = function() { - return jspb.Message.getField(this, 11) != null; -}; - - -/** - * optional int64 period = 12; - * @return {number} - */ -proto.google.v1.Profile.prototype.getPeriod = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 12, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.setPeriod = function(value) { - return jspb.Message.setProto3IntField(this, 12, value); -}; - - -/** - * repeated int64 comment = 13; - * @return {!Array<number>} - */ -proto.google.v1.Profile.prototype.getCommentList = function() { - return /** @type {!Array<number>} */ (jspb.Message.getRepeatedField(this, 13)); -}; - - -/** - * @param {!Array<number>} value - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.setCommentList = function(value) { - return jspb.Message.setField(this, 13, value || []); -}; - - -/** - * @param {number} value - * @param {number=} opt_index - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.addComment = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 13, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.clearCommentList = function() { - return this.setCommentList([]); -}; - - -/** - * optional int64 default_sample_type = 14; - * @return {number} - */ -proto.google.v1.Profile.prototype.getDefaultSampleType = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 14, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Profile} returns this - */ -proto.google.v1.Profile.prototype.setDefaultSampleType = function(value) { - return jspb.Message.setProto3IntField(this, 14, value); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.google.v1.ValueType.prototype.toObject = function(opt_includeInstance) { - return proto.google.v1.ValueType.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.google.v1.ValueType} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.google.v1.ValueType.toObject = function(includeInstance, msg) { - var f, obj = { - type: jspb.Message.getFieldWithDefault(msg, 1, 0), - unit: jspb.Message.getFieldWithDefault(msg, 2, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.google.v1.ValueType} - */ -proto.google.v1.ValueType.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.google.v1.ValueType; - return proto.google.v1.ValueType.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.google.v1.ValueType} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.google.v1.ValueType} - */ -proto.google.v1.ValueType.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt64()); - msg.setType(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt64()); - msg.setUnit(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.google.v1.ValueType.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.google.v1.ValueType.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.google.v1.ValueType} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.google.v1.ValueType.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getType(); - if (f !== 0) { - writer.writeInt64( - 1, - f - ); - } - f = message.getUnit(); - if (f !== 0) { - writer.writeInt64( - 2, - f - ); - } -}; - - -/** - * optional int64 type = 1; - * @return {number} - */ -proto.google.v1.ValueType.prototype.getType = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.ValueType} returns this - */ -proto.google.v1.ValueType.prototype.setType = function(value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - - -/** - * optional int64 unit = 2; - * @return {number} - */ -proto.google.v1.ValueType.prototype.getUnit = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.ValueType} returns this - */ -proto.google.v1.ValueType.prototype.setUnit = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.google.v1.Sample.repeatedFields_ = [1,2,3]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.google.v1.Sample.prototype.toObject = function(opt_includeInstance) { - return proto.google.v1.Sample.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.google.v1.Sample} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.google.v1.Sample.toObject = function(includeInstance, msg) { - var f, obj = { - locationIdList: (f = jspb.Message.getRepeatedField(msg, 1)) == null ? undefined : f, - valueList: (f = jspb.Message.getRepeatedField(msg, 2)) == null ? undefined : f, - labelList: jspb.Message.toObjectList(msg.getLabelList(), - proto.google.v1.Label.toObject, includeInstance) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.google.v1.Sample} - */ -proto.google.v1.Sample.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.google.v1.Sample; - return proto.google.v1.Sample.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.google.v1.Sample} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.google.v1.Sample} - */ -proto.google.v1.Sample.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var values = /** @type {!Array<number>} */ (reader.isDelimited() ? reader.readPackedUint64() : [reader.readUint64()]); - for (var i = 0; i < values.length; i++) { - msg.addLocationId(values[i]); - } - break; - case 2: - var values = /** @type {!Array<number>} */ (reader.isDelimited() ? reader.readPackedInt64() : [reader.readInt64()]); - for (var i = 0; i < values.length; i++) { - msg.addValue(values[i]); - } - break; - case 3: - var value = new proto.google.v1.Label; - reader.readMessage(value,proto.google.v1.Label.deserializeBinaryFromReader); - msg.addLabel(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.google.v1.Sample.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.google.v1.Sample.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.google.v1.Sample} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.google.v1.Sample.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getLocationIdList(); - if (f.length > 0) { - writer.writePackedUint64( - 1, - f - ); - } - f = message.getValueList(); - if (f.length > 0) { - writer.writePackedInt64( - 2, - f - ); - } - f = message.getLabelList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 3, - f, - proto.google.v1.Label.serializeBinaryToWriter - ); - } -}; - - -/** - * repeated uint64 location_id = 1; - * @return {!Array<number>} - */ -proto.google.v1.Sample.prototype.getLocationIdList = function() { - return /** @type {!Array<number>} */ (jspb.Message.getRepeatedField(this, 1)); -}; - - -/** - * @param {!Array<number>} value - * @return {!proto.google.v1.Sample} returns this - */ -proto.google.v1.Sample.prototype.setLocationIdList = function(value) { - return jspb.Message.setField(this, 1, value || []); -}; - - -/** - * @param {number} value - * @param {number=} opt_index - * @return {!proto.google.v1.Sample} returns this - */ -proto.google.v1.Sample.prototype.addLocationId = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 1, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.google.v1.Sample} returns this - */ -proto.google.v1.Sample.prototype.clearLocationIdList = function() { - return this.setLocationIdList([]); -}; - - -/** - * repeated int64 value = 2; - * @return {!Array<number>} - */ -proto.google.v1.Sample.prototype.getValueList = function() { - return /** @type {!Array<number>} */ (jspb.Message.getRepeatedField(this, 2)); -}; - - -/** - * @param {!Array<number>} value - * @return {!proto.google.v1.Sample} returns this - */ -proto.google.v1.Sample.prototype.setValueList = function(value) { - return jspb.Message.setField(this, 2, value || []); -}; - - -/** - * @param {number} value - * @param {number=} opt_index - * @return {!proto.google.v1.Sample} returns this - */ -proto.google.v1.Sample.prototype.addValue = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 2, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.google.v1.Sample} returns this - */ -proto.google.v1.Sample.prototype.clearValueList = function() { - return this.setValueList([]); -}; - - -/** - * repeated Label label = 3; - * @return {!Array<!proto.google.v1.Label>} - */ -proto.google.v1.Sample.prototype.getLabelList = function() { - return /** @type{!Array<!proto.google.v1.Label>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.google.v1.Label, 3)); -}; - - -/** - * @param {!Array<!proto.google.v1.Label>} value - * @return {!proto.google.v1.Sample} returns this -*/ -proto.google.v1.Sample.prototype.setLabelList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 3, value); -}; - - -/** - * @param {!proto.google.v1.Label=} opt_value - * @param {number=} opt_index - * @return {!proto.google.v1.Label} - */ -proto.google.v1.Sample.prototype.addLabel = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 3, opt_value, proto.google.v1.Label, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.google.v1.Sample} returns this - */ -proto.google.v1.Sample.prototype.clearLabelList = function() { - return this.setLabelList([]); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.google.v1.Label.prototype.toObject = function(opt_includeInstance) { - return proto.google.v1.Label.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.google.v1.Label} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.google.v1.Label.toObject = function(includeInstance, msg) { - var f, obj = { - key: jspb.Message.getFieldWithDefault(msg, 1, 0), - str: jspb.Message.getFieldWithDefault(msg, 2, 0), - num: jspb.Message.getFieldWithDefault(msg, 3, 0), - numUnit: jspb.Message.getFieldWithDefault(msg, 4, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.google.v1.Label} - */ -proto.google.v1.Label.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.google.v1.Label; - return proto.google.v1.Label.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.google.v1.Label} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.google.v1.Label} - */ -proto.google.v1.Label.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt64()); - msg.setKey(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt64()); - msg.setStr(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt64()); - msg.setNum(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt64()); - msg.setNumUnit(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.google.v1.Label.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.google.v1.Label.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.google.v1.Label} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.google.v1.Label.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getKey(); - if (f !== 0) { - writer.writeInt64( - 1, - f - ); - } - f = message.getStr(); - if (f !== 0) { - writer.writeInt64( - 2, - f - ); - } - f = message.getNum(); - if (f !== 0) { - writer.writeInt64( - 3, - f - ); - } - f = message.getNumUnit(); - if (f !== 0) { - writer.writeInt64( - 4, - f - ); - } -}; - - -/** - * optional int64 key = 1; - * @return {number} - */ -proto.google.v1.Label.prototype.getKey = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Label} returns this - */ -proto.google.v1.Label.prototype.setKey = function(value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - - -/** - * optional int64 str = 2; - * @return {number} - */ -proto.google.v1.Label.prototype.getStr = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Label} returns this - */ -proto.google.v1.Label.prototype.setStr = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - -/** - * optional int64 num = 3; - * @return {number} - */ -proto.google.v1.Label.prototype.getNum = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Label} returns this - */ -proto.google.v1.Label.prototype.setNum = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional int64 num_unit = 4; - * @return {number} - */ -proto.google.v1.Label.prototype.getNumUnit = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Label} returns this - */ -proto.google.v1.Label.prototype.setNumUnit = function(value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.google.v1.Mapping.prototype.toObject = function(opt_includeInstance) { - return proto.google.v1.Mapping.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.google.v1.Mapping} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.google.v1.Mapping.toObject = function(includeInstance, msg) { - var f, obj = { - id: jspb.Message.getFieldWithDefault(msg, 1, 0), - memoryStart: jspb.Message.getFieldWithDefault(msg, 2, 0), - memoryLimit: jspb.Message.getFieldWithDefault(msg, 3, 0), - fileOffset: jspb.Message.getFieldWithDefault(msg, 4, 0), - filename: jspb.Message.getFieldWithDefault(msg, 5, 0), - buildId: jspb.Message.getFieldWithDefault(msg, 6, 0), - hasFunctions: jspb.Message.getBooleanFieldWithDefault(msg, 7, false), - hasFilenames: jspb.Message.getBooleanFieldWithDefault(msg, 8, false), - hasLineNumbers: jspb.Message.getBooleanFieldWithDefault(msg, 9, false), - hasInlineFrames: jspb.Message.getBooleanFieldWithDefault(msg, 10, false) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.google.v1.Mapping} - */ -proto.google.v1.Mapping.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.google.v1.Mapping; - return proto.google.v1.Mapping.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.google.v1.Mapping} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.google.v1.Mapping} - */ -proto.google.v1.Mapping.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readUint64()); - msg.setId(value); - break; - case 2: - var value = /** @type {number} */ (reader.readUint64()); - msg.setMemoryStart(value); - break; - case 3: - var value = /** @type {number} */ (reader.readUint64()); - msg.setMemoryLimit(value); - break; - case 4: - var value = /** @type {number} */ (reader.readUint64()); - msg.setFileOffset(value); - break; - case 5: - var value = /** @type {number} */ (reader.readInt64()); - msg.setFilename(value); - break; - case 6: - var value = /** @type {number} */ (reader.readInt64()); - msg.setBuildId(value); - break; - case 7: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setHasFunctions(value); - break; - case 8: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setHasFilenames(value); - break; - case 9: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setHasLineNumbers(value); - break; - case 10: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setHasInlineFrames(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.google.v1.Mapping.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.google.v1.Mapping.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.google.v1.Mapping} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.google.v1.Mapping.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getId(); - if (f !== 0) { - writer.writeUint64( - 1, - f - ); - } - f = message.getMemoryStart(); - if (f !== 0) { - writer.writeUint64( - 2, - f - ); - } - f = message.getMemoryLimit(); - if (f !== 0) { - writer.writeUint64( - 3, - f - ); - } - f = message.getFileOffset(); - if (f !== 0) { - writer.writeUint64( - 4, - f - ); - } - f = message.getFilename(); - if (f !== 0) { - writer.writeInt64( - 5, - f - ); - } - f = message.getBuildId(); - if (f !== 0) { - writer.writeInt64( - 6, - f - ); - } - f = message.getHasFunctions(); - if (f) { - writer.writeBool( - 7, - f - ); - } - f = message.getHasFilenames(); - if (f) { - writer.writeBool( - 8, - f - ); - } - f = message.getHasLineNumbers(); - if (f) { - writer.writeBool( - 9, - f - ); - } - f = message.getHasInlineFrames(); - if (f) { - writer.writeBool( - 10, - f - ); - } -}; - - -/** - * optional uint64 id = 1; - * @return {number} - */ -proto.google.v1.Mapping.prototype.getId = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Mapping} returns this - */ -proto.google.v1.Mapping.prototype.setId = function(value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - - -/** - * optional uint64 memory_start = 2; - * @return {number} - */ -proto.google.v1.Mapping.prototype.getMemoryStart = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Mapping} returns this - */ -proto.google.v1.Mapping.prototype.setMemoryStart = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - -/** - * optional uint64 memory_limit = 3; - * @return {number} - */ -proto.google.v1.Mapping.prototype.getMemoryLimit = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Mapping} returns this - */ -proto.google.v1.Mapping.prototype.setMemoryLimit = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional uint64 file_offset = 4; - * @return {number} - */ -proto.google.v1.Mapping.prototype.getFileOffset = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Mapping} returns this - */ -proto.google.v1.Mapping.prototype.setFileOffset = function(value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - - -/** - * optional int64 filename = 5; - * @return {number} - */ -proto.google.v1.Mapping.prototype.getFilename = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Mapping} returns this - */ -proto.google.v1.Mapping.prototype.setFilename = function(value) { - return jspb.Message.setProto3IntField(this, 5, value); -}; - - -/** - * optional int64 build_id = 6; - * @return {number} - */ -proto.google.v1.Mapping.prototype.getBuildId = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Mapping} returns this - */ -proto.google.v1.Mapping.prototype.setBuildId = function(value) { - return jspb.Message.setProto3IntField(this, 6, value); -}; - - -/** - * optional bool has_functions = 7; - * @return {boolean} - */ -proto.google.v1.Mapping.prototype.getHasFunctions = function() { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 7, false)); -}; - - -/** - * @param {boolean} value - * @return {!proto.google.v1.Mapping} returns this - */ -proto.google.v1.Mapping.prototype.setHasFunctions = function(value) { - return jspb.Message.setProto3BooleanField(this, 7, value); -}; - - -/** - * optional bool has_filenames = 8; - * @return {boolean} - */ -proto.google.v1.Mapping.prototype.getHasFilenames = function() { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 8, false)); -}; - - -/** - * @param {boolean} value - * @return {!proto.google.v1.Mapping} returns this - */ -proto.google.v1.Mapping.prototype.setHasFilenames = function(value) { - return jspb.Message.setProto3BooleanField(this, 8, value); -}; - - -/** - * optional bool has_line_numbers = 9; - * @return {boolean} - */ -proto.google.v1.Mapping.prototype.getHasLineNumbers = function() { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 9, false)); -}; - - -/** - * @param {boolean} value - * @return {!proto.google.v1.Mapping} returns this - */ -proto.google.v1.Mapping.prototype.setHasLineNumbers = function(value) { - return jspb.Message.setProto3BooleanField(this, 9, value); -}; - - -/** - * optional bool has_inline_frames = 10; - * @return {boolean} - */ -proto.google.v1.Mapping.prototype.getHasInlineFrames = function() { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 10, false)); -}; - - -/** - * @param {boolean} value - * @return {!proto.google.v1.Mapping} returns this - */ -proto.google.v1.Mapping.prototype.setHasInlineFrames = function(value) { - return jspb.Message.setProto3BooleanField(this, 10, value); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.google.v1.Location.repeatedFields_ = [4]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.google.v1.Location.prototype.toObject = function(opt_includeInstance) { - return proto.google.v1.Location.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.google.v1.Location} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.google.v1.Location.toObject = function(includeInstance, msg) { - var f, obj = { - id: jspb.Message.getFieldWithDefault(msg, 1, 0), - mappingId: jspb.Message.getFieldWithDefault(msg, 2, 0), - address: jspb.Message.getFieldWithDefault(msg, 3, 0), - lineList: jspb.Message.toObjectList(msg.getLineList(), - proto.google.v1.Line.toObject, includeInstance), - isFolded: jspb.Message.getBooleanFieldWithDefault(msg, 5, false) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.google.v1.Location} - */ -proto.google.v1.Location.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.google.v1.Location; - return proto.google.v1.Location.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.google.v1.Location} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.google.v1.Location} - */ -proto.google.v1.Location.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readUint64()); - msg.setId(value); - break; - case 2: - var value = /** @type {number} */ (reader.readUint64()); - msg.setMappingId(value); - break; - case 3: - var value = /** @type {number} */ (reader.readUint64()); - msg.setAddress(value); - break; - case 4: - var value = new proto.google.v1.Line; - reader.readMessage(value,proto.google.v1.Line.deserializeBinaryFromReader); - msg.addLine(value); - break; - case 5: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setIsFolded(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.google.v1.Location.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.google.v1.Location.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.google.v1.Location} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.google.v1.Location.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getId(); - if (f !== 0) { - writer.writeUint64( - 1, - f - ); - } - f = message.getMappingId(); - if (f !== 0) { - writer.writeUint64( - 2, - f - ); - } - f = message.getAddress(); - if (f !== 0) { - writer.writeUint64( - 3, - f - ); - } - f = message.getLineList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 4, - f, - proto.google.v1.Line.serializeBinaryToWriter - ); - } - f = message.getIsFolded(); - if (f) { - writer.writeBool( - 5, - f - ); - } -}; - - -/** - * optional uint64 id = 1; - * @return {number} - */ -proto.google.v1.Location.prototype.getId = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Location} returns this - */ -proto.google.v1.Location.prototype.setId = function(value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - - -/** - * optional uint64 mapping_id = 2; - * @return {number} - */ -proto.google.v1.Location.prototype.getMappingId = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Location} returns this - */ -proto.google.v1.Location.prototype.setMappingId = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - -/** - * optional uint64 address = 3; - * @return {number} - */ -proto.google.v1.Location.prototype.getAddress = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Location} returns this - */ -proto.google.v1.Location.prototype.setAddress = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * repeated Line line = 4; - * @return {!Array<!proto.google.v1.Line>} - */ -proto.google.v1.Location.prototype.getLineList = function() { - return /** @type{!Array<!proto.google.v1.Line>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.google.v1.Line, 4)); -}; - - -/** - * @param {!Array<!proto.google.v1.Line>} value - * @return {!proto.google.v1.Location} returns this -*/ -proto.google.v1.Location.prototype.setLineList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 4, value); -}; - - -/** - * @param {!proto.google.v1.Line=} opt_value - * @param {number=} opt_index - * @return {!proto.google.v1.Line} - */ -proto.google.v1.Location.prototype.addLine = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 4, opt_value, proto.google.v1.Line, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.google.v1.Location} returns this - */ -proto.google.v1.Location.prototype.clearLineList = function() { - return this.setLineList([]); -}; - - -/** - * optional bool is_folded = 5; - * @return {boolean} - */ -proto.google.v1.Location.prototype.getIsFolded = function() { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 5, false)); -}; - - -/** - * @param {boolean} value - * @return {!proto.google.v1.Location} returns this - */ -proto.google.v1.Location.prototype.setIsFolded = function(value) { - return jspb.Message.setProto3BooleanField(this, 5, value); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.google.v1.Line.prototype.toObject = function(opt_includeInstance) { - return proto.google.v1.Line.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.google.v1.Line} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.google.v1.Line.toObject = function(includeInstance, msg) { - var f, obj = { - functionId: jspb.Message.getFieldWithDefault(msg, 1, 0), - line: jspb.Message.getFieldWithDefault(msg, 2, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.google.v1.Line} - */ -proto.google.v1.Line.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.google.v1.Line; - return proto.google.v1.Line.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.google.v1.Line} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.google.v1.Line} - */ -proto.google.v1.Line.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readUint64()); - msg.setFunctionId(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt64()); - msg.setLine(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.google.v1.Line.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.google.v1.Line.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.google.v1.Line} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.google.v1.Line.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getFunctionId(); - if (f !== 0) { - writer.writeUint64( - 1, - f - ); - } - f = message.getLine(); - if (f !== 0) { - writer.writeInt64( - 2, - f - ); - } -}; - - -/** - * optional uint64 function_id = 1; - * @return {number} - */ -proto.google.v1.Line.prototype.getFunctionId = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Line} returns this - */ -proto.google.v1.Line.prototype.setFunctionId = function(value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - - -/** - * optional int64 line = 2; - * @return {number} - */ -proto.google.v1.Line.prototype.getLine = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Line} returns this - */ -proto.google.v1.Line.prototype.setLine = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.google.v1.Function.prototype.toObject = function(opt_includeInstance) { - return proto.google.v1.Function.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.google.v1.Function} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.google.v1.Function.toObject = function(includeInstance, msg) { - var f, obj = { - id: jspb.Message.getFieldWithDefault(msg, 1, 0), - name: jspb.Message.getFieldWithDefault(msg, 2, 0), - systemName: jspb.Message.getFieldWithDefault(msg, 3, 0), - filename: jspb.Message.getFieldWithDefault(msg, 4, 0), - startLine: jspb.Message.getFieldWithDefault(msg, 5, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.google.v1.Function} - */ -proto.google.v1.Function.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.google.v1.Function; - return proto.google.v1.Function.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.google.v1.Function} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.google.v1.Function} - */ -proto.google.v1.Function.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readUint64()); - msg.setId(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt64()); - msg.setName(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt64()); - msg.setSystemName(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt64()); - msg.setFilename(value); - break; - case 5: - var value = /** @type {number} */ (reader.readInt64()); - msg.setStartLine(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.google.v1.Function.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.google.v1.Function.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.google.v1.Function} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.google.v1.Function.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getId(); - if (f !== 0) { - writer.writeUint64( - 1, - f - ); - } - f = message.getName(); - if (f !== 0) { - writer.writeInt64( - 2, - f - ); - } - f = message.getSystemName(); - if (f !== 0) { - writer.writeInt64( - 3, - f - ); - } - f = message.getFilename(); - if (f !== 0) { - writer.writeInt64( - 4, - f - ); - } - f = message.getStartLine(); - if (f !== 0) { - writer.writeInt64( - 5, - f - ); - } -}; - - -/** - * optional uint64 id = 1; - * @return {number} - */ -proto.google.v1.Function.prototype.getId = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Function} returns this - */ -proto.google.v1.Function.prototype.setId = function(value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - - -/** - * optional int64 name = 2; - * @return {number} - */ -proto.google.v1.Function.prototype.getName = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Function} returns this - */ -proto.google.v1.Function.prototype.setName = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - -/** - * optional int64 system_name = 3; - * @return {number} - */ -proto.google.v1.Function.prototype.getSystemName = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Function} returns this - */ -proto.google.v1.Function.prototype.setSystemName = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional int64 filename = 4; - * @return {number} - */ -proto.google.v1.Function.prototype.getFilename = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Function} returns this - */ -proto.google.v1.Function.prototype.setFilename = function(value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - - -/** - * optional int64 start_line = 5; - * @return {number} - */ -proto.google.v1.Function.prototype.getStartLine = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.google.v1.Function} returns this - */ -proto.google.v1.Function.prototype.setStartLine = function(value) { - return jspb.Message.setProto3IntField(this, 5, value); -}; - - -goog.object.extend(exports, proto.google.v1); diff --git a/pyroscope/json_parsers.js b/pyroscope/json_parsers.js deleted file mode 100644 index f7fa9d0a..00000000 --- a/pyroscope/json_parsers.js +++ /dev/null @@ -1,71 +0,0 @@ -const { bufferize } = require('./shared') - -/** - * - * @param req - */ -const series = async (req, payload) => { - let body = await bufferize(payload) - body = JSON.parse(body.toString()) - req.type = 'json' - return { - getStart: () => body.start, - getEnd: () => body.end, - getMatchersList: () => body.matchers, - getLabelNamesList: () => body.labelNames - } -} - -const getProfileStats = async (req, payload) => { - req.type = 'json' - return null -} - -const settingsGet = async (req, payload) => { - req.type = 'json' - return {} -} - -const labelNames = async (req, payload) => { - req.type = 'json' - let body = await bufferize(payload) - body = JSON.parse(body.toString()) - return { - getStart: () => body.start, - getEnd: () => body.end, - getName: () => body.name, - getMatchersList: () => body.matchers - } -} - -const labelValues = async (req, payload) => { - req.type = 'json' - let body = await bufferize(payload) - body = JSON.parse(body.toString()) - return { - getName: () => body.name, - getMatchersList: () => body.matchers, - getStart: () => body.start, - getEnd: () => body.end - } -} - -const analyzeQuery = async (req, payload) => { - req.type = 'json' - let body = await bufferize(payload) - body = JSON.parse(body.toString()) - return { - getStart: () => body.start, - getEnd: () => body.end, - getQuery: () => body.query - } -} - -module.exports = { - series, - getProfileStats, - labelNames, - labelValues, - settingsGet, - analyzeQuery -} diff --git a/pyroscope/merge_stack_traces.js b/pyroscope/merge_stack_traces.js deleted file mode 100644 index e4654592..00000000 --- a/pyroscope/merge_stack_traces.js +++ /dev/null @@ -1,171 +0,0 @@ -const { checkVersion, DATABASE_NAME } = require('../lib/utils') -const Sql = require('@cloki/clickhouse-sql') -const { clusterName } = require('../common') -const clickhouse = require('../lib/db/clickhouse') -const { readULeb32 } = require('./pprof') -const pprofBin = require('./pprof-bin/pkg') -const { - serviceNameSelectorQuery, - labelSelectorQuery -} = require('./shared') - -const sqlWithReference = (ref) => { - const res = new Sql.WithReference(ref) - res.toString = function () { - if (this.ref.inline) { - return `(${this.ref.query.toString()}) as ${this.ref.alias}` - } - return this.ref.alias - } - return res -} - -let ctxIdx = 0 - -const newCtxIdx = () => ++ctxIdx - -const importStackTraces = async (typeRegex, sel, fromTimeSec, toTimeSec, log, _ctxIdx, save) => { - const dist = clusterName ? '_dist' : '' - const v2 = checkVersion('profiles_v2', (fromTimeSec - 3600) * 1000) - const serviceNameSelector = serviceNameSelectorQuery(sel) - const typeIdSelector = Sql.Eq( - 'type_id', - Sql.val(`${typeRegex.type}:${typeRegex.periodType}:${typeRegex.periodUnit}`) - ) - const idxSelect = (new Sql.Select()) - .select('fingerprint') - .from(`${DATABASE_NAME()}.profiles_series_gin`) - .where( - Sql.And( - Sql.Eq(new Sql.Raw(`has(sample_types_units, (${Sql.quoteVal(typeRegex.sampleType)},${Sql.quoteVal(typeRegex.sampleUnit)}))`), 1), - typeIdSelector, - Sql.Gte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)}))`)), - Sql.Lte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)}))`)), - serviceNameSelector - ) - ).groupBy('fingerprint') - labelSelectorQuery(idxSelect, sel) - const withIdxSelect = new Sql.With('idx', idxSelect, !!clusterName) - const rawReq = (new Sql.Select()).with(withIdxSelect) - .select([ - new Sql.Raw(`arrayMap(x -> (x.1, x.2, x.3, (arrayFirst(y -> y.1 == ${Sql.quoteVal(`${typeRegex.sampleType}:${typeRegex.sampleUnit}`)}, x.4) as af).2, af.3), tree)`), - 'tree' - ], 'functions') - .from(`${DATABASE_NAME()}.profiles${dist}`) - .where( - Sql.And( - Sql.Gte('timestamp_ns', new Sql.Raw(Math.floor(fromTimeSec) + '000000000')), - Sql.Lte('timestamp_ns', new Sql.Raw(Math.floor(toTimeSec) + '000000000')), - new Sql.In('fingerprint', 'IN', sqlWithReference(withIdxSelect)), - typeIdSelector, - serviceNameSelector - )) - if (process.env.ADVANCED_PROFILES_MERGE_LIMIT) { - rawReq.orderBy(['timestamp_ns', 'desc']).limit(parseInt(process.env.ADVANCED_PROFILES_MERGE_LIMIT)) - } - const withRawReq = new Sql.With('raw', rawReq, !!clusterName) - const joinedReq = (new Sql.Select()).with(withRawReq).select([ - new Sql.Raw('(raw.tree.1, raw.tree.2, raw.tree.3, sum(raw.tree.4), sum(raw.tree.5))'), - 'tree2' - ]).from(sqlWithReference(withRawReq)) - .join('raw.tree', 'array') - .groupBy(new Sql.Raw('raw.tree.1'), new Sql.Raw('raw.tree.2'), new Sql.Raw('raw.tree.3')) - .orderBy(new Sql.Raw('raw.tree.1')).limit(2000000) - const withJoinedReq = new Sql.With('joined', joinedReq, !!clusterName) - const joinedAggregatedReq = (new Sql.Select()).select( - [new Sql.Raw('groupArray(tree2)'), 'tree']).from(sqlWithReference(withJoinedReq)) - const functionsReq = (new Sql.Select()).select( - [new Sql.Raw('groupUniqArray(raw.functions)'), 'functions2'] - ).from(sqlWithReference(withRawReq)).join('raw.functions', 'array') - - let brackLegacy = (new Sql.Select()).select( - [new Sql.Raw('[]::Array(String)'), 'legacy'] - ) - let withLegacy = null - if (!v2) { - const legacy = (new Sql.Select()).with(withIdxSelect) - .select('payload') - .from(`${DATABASE_NAME()}.profiles${dist}`) - .where( - Sql.And( - Sql.Gte('timestamp_ns', new Sql.Raw(Math.floor(fromTimeSec) + '000000000')), - Sql.Lte('timestamp_ns', new Sql.Raw(Math.floor(toTimeSec) + '000000000')), - new Sql.In('fingerprint', 'IN', sqlWithReference(withIdxSelect)), - Sql.Eq(new Sql.Raw('empty(tree)'), 1), - typeIdSelector, - serviceNameSelector - )) - if (process.env.ADVANCED_PROFILES_MERGE_LIMIT) { - legacy.orderBy(['timestamp_ns', 'desc']).limit(parseInt(process.env.ADVANCED_PROFILES_MERGE_LIMIT)) - } - withLegacy = new Sql.With('legacy', legacy, !!clusterName) - brackLegacy = (new Sql.Select()) - .select([new Sql.Raw('groupArray(payload)'), 'payloads']) - .from(sqlWithReference(withLegacy)) - } - brackLegacy = new Sql.Raw(`(${brackLegacy.toString()})`) - const brack1 = new Sql.Raw(`(${joinedAggregatedReq.toString()})`) - const brack2 = new Sql.Raw(`(${functionsReq.toString()})`) - - const sqlReq = (new Sql.Select()) - .select( - [brackLegacy, 'legacy'], - [brack2, 'functions'], - [brack1, 'tree'] - ) - if (v2) { - sqlReq.with(withJoinedReq, withRawReq) - } else { - sqlReq.with(withJoinedReq, withRawReq, withLegacy) - } - - let start = Date.now() - const profiles = await clickhouse.rawRequest(sqlReq.toString() + ' FORMAT RowBinary', - null, - DATABASE_NAME(), - { - responseType: 'arraybuffer' - }) - const binData = Uint8Array.from(profiles.data) - log.debug(`selectMergeStacktraces: profiles downloaded: ${binData.length / 1025}kB in ${Date.now() - start}ms`) - require('./pprof-bin/pkg/pprof_bin').init_panic_hook() - const [legacyLen, shift] = readULeb32(binData, 0) - let ofs = shift - let mergePprofLat = BigInt(0) - for (let i = 0; i < legacyLen; i++) { - const [profLen, shift] = readULeb32(binData, ofs) - ofs += shift - start = process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0) - pprofBin.merge_prof(_ctxIdx, - Uint8Array.from(profiles.data.slice(ofs, ofs + profLen)), - `${typeRegex.sampleType}:${typeRegex.sampleUnit}`) - mergePprofLat += (process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0)) - start - ofs += profLen - } - start = process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0) - pprofBin.merge_tree(_ctxIdx, Uint8Array.from(profiles.data.slice(ofs)), - typeRegex.sampleType + ':' + typeRegex.sampleUnit) - const mergeTreeLat = (process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0)) - start - log.debug(`merge_pprof: ${mergePprofLat / BigInt(1000000)}ms`) - log.debug(`merge_tree: ${mergeTreeLat / BigInt(1000000)}ms`) -} - -const mergeStackTraces = async (typeRegex, sel, fromTimeSec, toTimeSec, log) => { - const _ctxIdx = newCtxIdx() - try { - await importStackTraces(typeRegex, sel, fromTimeSec, toTimeSec, log, _ctxIdx) - const start = process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0) - const resp = pprofBin.export_tree(_ctxIdx, typeRegex.sampleType + ':' + typeRegex.sampleUnit) - const exportTreeLat = (process.hrtime?.bigint ? process.hrtime.bigint() : BigInt(0)) - start - log.debug(`export_tree: ${exportTreeLat / BigInt(1000000)}ms`) - return Buffer.from(resp) - } finally { - try { pprofBin.drop_tree(_ctxIdx) } catch (e) {} - } -} - -module.exports = { - mergeStackTraces, - importStackTraces, - newCtxIdx -} diff --git a/pyroscope/pprof-bin/.gitignore b/pyroscope/pprof-bin/.gitignore deleted file mode 100644 index f747c98a..00000000 --- a/pyroscope/pprof-bin/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -/target -**/*.rs.bk -Cargo.lock -bin/ -wasm-pack.log -.appveyor.yml -.travis.yml -README.md diff --git a/pyroscope/pprof-bin/Cargo.toml b/pyroscope/pprof-bin/Cargo.toml deleted file mode 100644 index 0dc29124..00000000 --- a/pyroscope/pprof-bin/Cargo.toml +++ /dev/null @@ -1,55 +0,0 @@ -[package] -name = "pprof-bin" -version = "0.1.0" -authors = ["akvlad90@gmail.com"] -edition = "2018" -build = "build.rs" - - -[lib] -crate-type = ["cdylib", "rlib"] - -[features] -default = ["console_error_panic_hook"] - -[dependencies] -wasm-bindgen = "0.2.84" -bytes = "1.5.0" -prost = "0.12.3" -json = "0.12.4" -lazy_static = "1.4.0" -bytemuck = "1.16.1" -flate2 = "1.0" - -# The `console_error_panic_hook` crate provides better debugging of panics by -# logging them with `console.error`. This is great for development, but requires -# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for -# code size when deploying. -console_error_panic_hook = { version = "0.1.7", optional = true } -base64 = "0.22.1" -memchr = "2.7.4" - -[dev-dependencies] -wasm-bindgen-test = "0.3.34" -criterion = { version = "0.5.1", features = ["html_reports"] } - -[[bench]] -name = "my_benchmark" -harness = false - -[profile.release] -# Tell `rustc` to optimize for small code size. -opt-level = "s" -overflow-checks = false - -[profile.dev] -overflow-checks = false - - -[build-dependencies] -prost-build = { version = "0.12.3" } - -[dependencies.web-sys] -version = "0.3.67" -features = ["console"] - diff --git a/pyroscope/pprof-bin/Makefile b/pyroscope/pprof-bin/Makefile deleted file mode 100644 index 64e718a9..00000000 --- a/pyroscope/pprof-bin/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -.PHONY: build - -build: - wasm-pack build --target nodejs - rm -rf pkg/.gitignore pkg/README.md diff --git a/pyroscope/pprof-bin/benches/my_benchmark.rs b/pyroscope/pprof-bin/benches/my_benchmark.rs deleted file mode 100644 index 0b6f988f..00000000 --- a/pyroscope/pprof-bin/benches/my_benchmark.rs +++ /dev/null @@ -1,18 +0,0 @@ -use pprof_bin::merge_prof; -use pprof_bin::utest::get_test_pprof_data; -use criterion::{black_box, criterion_group, criterion_main, Criterion}; - -fn merge_bench(pprofs: &Vec<Vec<u8>>) { - - for pprof in pprofs { - merge_prof(0, pprof.as_slice(), "process_cpu:samples:count:cpu:nanoseconds".to_string()); - } -} - -fn criterion_benchmark(c: &mut Criterion) { - let pprofs = get_test_pprof_data(); - c.bench_function("merge", |b| b.iter(|| merge_bench(&pprofs))); -} - -criterion_group!(benches, criterion_benchmark); -criterion_main!(benches); \ No newline at end of file diff --git a/pyroscope/pprof-bin/build.rs b/pyroscope/pprof-bin/build.rs deleted file mode 100644 index f02ce147..00000000 --- a/pyroscope/pprof-bin/build.rs +++ /dev/null @@ -1,6 +0,0 @@ -use std::io::Result; - -fn main() -> Result<()> { - prost_build::compile_protos(&["../proto/querier.proto"], &["../proto"])?; - Ok(()) -} diff --git a/pyroscope/pprof-bin/pkg/package.json b/pyroscope/pprof-bin/pkg/package.json deleted file mode 100644 index a2cf499e..00000000 --- a/pyroscope/pprof-bin/pkg/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "pprof-bin", - "collaborators": [ - "akvlad90@gmail.com" - ], - "version": "0.1.0", - "files": [ - "pprof_bin_bg.wasm", - "pprof_bin.js", - "pprof_bin.d.ts" - ], - "main": "pprof_bin.js", - "types": "pprof_bin.d.ts" -} \ No newline at end of file diff --git a/pyroscope/pprof-bin/pkg/pprof_bin.d.ts b/pyroscope/pprof-bin/pkg/pprof_bin.d.ts deleted file mode 100644 index 39e35601..00000000 --- a/pyroscope/pprof-bin/pkg/pprof_bin.d.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -/** -* @param {number} id -* @param {Uint8Array} bytes -* @param {string} sample_type -*/ -export function merge_prof(id: number, bytes: Uint8Array, sample_type: string): void; -/** -* @param {number} id -* @param {Uint8Array} bytes -* @param {string} sample_type -*/ -export function merge_tree(id: number, bytes: Uint8Array, sample_type: string): void; -/** -* @param {number} id1 -* @param {number} id2 -* @param {string} sample_type -* @returns {Uint8Array} -*/ -export function diff_tree(id1: number, id2: number, sample_type: string): Uint8Array; -/** -* @param {number} id -* @param {string} sample_type -* @returns {Uint8Array} -*/ -export function export_tree(id: number, sample_type: string): Uint8Array; -/** -* @param {number} id -* @param {Uint8Array} payload -*/ -export function merge_trees_pprof(id: number, payload: Uint8Array): void; -/** -* @param {number} id -* @returns {Uint8Array} -*/ -export function export_trees_pprof(id: number): Uint8Array; -/** -* @param {number} id -*/ -export function drop_tree(id: number): void; -/** -*/ -export function init_panic_hook(): void; diff --git a/pyroscope/pprof-bin/pkg/pprof_bin.js b/pyroscope/pprof-bin/pkg/pprof_bin.js deleted file mode 100644 index e9a9781b..00000000 --- a/pyroscope/pprof-bin/pkg/pprof_bin.js +++ /dev/null @@ -1,274 +0,0 @@ -let imports = {}; -imports['__wbindgen_placeholder__'] = module.exports; -let wasm; -const { TextEncoder, TextDecoder } = require(`util`); - -const heap = new Array(128).fill(undefined); - -heap.push(undefined, null, true, false); - -function getObject(idx) { return heap[idx]; } - -let heap_next = heap.length; - -function dropObject(idx) { - if (idx < 132) return; - heap[idx] = heap_next; - heap_next = idx; -} - -function takeObject(idx) { - const ret = getObject(idx); - dropObject(idx); - return ret; -} - -let cachedUint8Memory0 = null; - -function getUint8Memory0() { - if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) { - cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); - } - return cachedUint8Memory0; -} - -let WASM_VECTOR_LEN = 0; - -function passArray8ToWasm0(arg, malloc) { - const ptr = malloc(arg.length * 1, 1) >>> 0; - getUint8Memory0().set(arg, ptr / 1); - WASM_VECTOR_LEN = arg.length; - return ptr; -} - -let cachedTextEncoder = new TextEncoder('utf-8'); - -const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' - ? function (arg, view) { - return cachedTextEncoder.encodeInto(arg, view); -} - : function (arg, view) { - const buf = cachedTextEncoder.encode(arg); - view.set(buf); - return { - read: arg.length, - written: buf.length - }; -}); - -function passStringToWasm0(arg, malloc, realloc) { - - if (realloc === undefined) { - const buf = cachedTextEncoder.encode(arg); - const ptr = malloc(buf.length, 1) >>> 0; - getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); - WASM_VECTOR_LEN = buf.length; - return ptr; - } - - let len = arg.length; - let ptr = malloc(len, 1) >>> 0; - - const mem = getUint8Memory0(); - - let offset = 0; - - for (; offset < len; offset++) { - const code = arg.charCodeAt(offset); - if (code > 0x7F) break; - mem[ptr + offset] = code; - } - - if (offset !== len) { - if (offset !== 0) { - arg = arg.slice(offset); - } - ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; - const view = getUint8Memory0().subarray(ptr + offset, ptr + len); - const ret = encodeString(arg, view); - - offset += ret.written; - ptr = realloc(ptr, len, offset, 1) >>> 0; - } - - WASM_VECTOR_LEN = offset; - return ptr; -} -/** -* @param {number} id -* @param {Uint8Array} bytes -* @param {string} sample_type -*/ -module.exports.merge_prof = function(id, bytes, sample_type) { - const ptr0 = passArray8ToWasm0(bytes, wasm.__wbindgen_malloc); - const len0 = WASM_VECTOR_LEN; - const ptr1 = passStringToWasm0(sample_type, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len1 = WASM_VECTOR_LEN; - wasm.merge_prof(id, ptr0, len0, ptr1, len1); -}; - -/** -* @param {number} id -* @param {Uint8Array} bytes -* @param {string} sample_type -*/ -module.exports.merge_tree = function(id, bytes, sample_type) { - const ptr0 = passArray8ToWasm0(bytes, wasm.__wbindgen_malloc); - const len0 = WASM_VECTOR_LEN; - const ptr1 = passStringToWasm0(sample_type, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len1 = WASM_VECTOR_LEN; - wasm.merge_tree(id, ptr0, len0, ptr1, len1); -}; - -let cachedInt32Memory0 = null; - -function getInt32Memory0() { - if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) { - cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); - } - return cachedInt32Memory0; -} - -function getArrayU8FromWasm0(ptr, len) { - ptr = ptr >>> 0; - return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len); -} -/** -* @param {number} id1 -* @param {number} id2 -* @param {string} sample_type -* @returns {Uint8Array} -*/ -module.exports.diff_tree = function(id1, id2, sample_type) { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = passStringToWasm0(sample_type, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - wasm.diff_tree(retptr, id1, id2, ptr0, len0); - var r0 = getInt32Memory0()[retptr / 4 + 0]; - var r1 = getInt32Memory0()[retptr / 4 + 1]; - var v2 = getArrayU8FromWasm0(r0, r1).slice(); - wasm.__wbindgen_free(r0, r1 * 1, 1); - return v2; - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - } -}; - -/** -* @param {number} id -* @param {string} sample_type -* @returns {Uint8Array} -*/ -module.exports.export_tree = function(id, sample_type) { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = passStringToWasm0(sample_type, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - wasm.export_tree(retptr, id, ptr0, len0); - var r0 = getInt32Memory0()[retptr / 4 + 0]; - var r1 = getInt32Memory0()[retptr / 4 + 1]; - var v2 = getArrayU8FromWasm0(r0, r1).slice(); - wasm.__wbindgen_free(r0, r1 * 1, 1); - return v2; - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - } -}; - -/** -* @param {number} id -* @param {Uint8Array} payload -*/ -module.exports.merge_trees_pprof = function(id, payload) { - const ptr0 = passArray8ToWasm0(payload, wasm.__wbindgen_malloc); - const len0 = WASM_VECTOR_LEN; - wasm.merge_trees_pprof(id, ptr0, len0); -}; - -/** -* @param {number} id -* @returns {Uint8Array} -*/ -module.exports.export_trees_pprof = function(id) { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - wasm.export_trees_pprof(retptr, id); - var r0 = getInt32Memory0()[retptr / 4 + 0]; - var r1 = getInt32Memory0()[retptr / 4 + 1]; - var v1 = getArrayU8FromWasm0(r0, r1).slice(); - wasm.__wbindgen_free(r0, r1 * 1, 1); - return v1; - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - } -}; - -/** -* @param {number} id -*/ -module.exports.drop_tree = function(id) { - wasm.drop_tree(id); -}; - -/** -*/ -module.exports.init_panic_hook = function() { - wasm.init_panic_hook(); -}; - -let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); - -cachedTextDecoder.decode(); - -function getStringFromWasm0(ptr, len) { - ptr = ptr >>> 0; - return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); -} - -function addHeapObject(obj) { - if (heap_next === heap.length) heap.push(heap.length + 1); - const idx = heap_next; - heap_next = heap[idx]; - - heap[idx] = obj; - return idx; -} - -module.exports.__wbg_new_abda76e883ba8a5f = function() { - const ret = new Error(); - return addHeapObject(ret); -}; - -module.exports.__wbg_stack_658279fe44541cf6 = function(arg0, arg1) { - const ret = getObject(arg1).stack; - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len1 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len1; - getInt32Memory0()[arg0 / 4 + 0] = ptr1; -}; - -module.exports.__wbg_error_f851667af71bcfc6 = function(arg0, arg1) { - let deferred0_0; - let deferred0_1; - try { - deferred0_0 = arg0; - deferred0_1 = arg1; - console.error(getStringFromWasm0(arg0, arg1)); - } finally { - wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); - } -}; - -module.exports.__wbindgen_object_drop_ref = function(arg0) { - takeObject(arg0); -}; - -const path = require('path').join(__dirname, 'pprof_bin_bg.wasm'); -const bytes = require('fs').readFileSync(path); - -const wasmModule = new WebAssembly.Module(bytes); -const wasmInstance = new WebAssembly.Instance(wasmModule, imports); -wasm = wasmInstance.exports; -module.exports.__wasm = wasm; - diff --git a/pyroscope/pprof-bin/pkg/pprof_bin_bg.wasm b/pyroscope/pprof-bin/pkg/pprof_bin_bg.wasm deleted file mode 100644 index 0ee3ff45..00000000 Binary files a/pyroscope/pprof-bin/pkg/pprof_bin_bg.wasm and /dev/null differ diff --git a/pyroscope/pprof-bin/pkg/pprof_bin_bg.wasm.d.ts b/pyroscope/pprof-bin/pkg/pprof_bin_bg.wasm.d.ts deleted file mode 100644 index 8947ed29..00000000 --- a/pyroscope/pprof-bin/pkg/pprof_bin_bg.wasm.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -export const memory: WebAssembly.Memory; -export function merge_prof(a: number, b: number, c: number, d: number, e: number): void; -export function merge_tree(a: number, b: number, c: number, d: number, e: number): void; -export function diff_tree(a: number, b: number, c: number, d: number, e: number): void; -export function export_tree(a: number, b: number, c: number, d: number): void; -export function merge_trees_pprof(a: number, b: number, c: number): void; -export function export_trees_pprof(a: number, b: number): void; -export function drop_tree(a: number): void; -export function init_panic_hook(): void; -export function __wbindgen_malloc(a: number, b: number): number; -export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number; -export function __wbindgen_add_to_stack_pointer(a: number): number; -export function __wbindgen_free(a: number, b: number, c: number): void; diff --git a/pyroscope/pprof-bin/src/ch64.rs b/pyroscope/pprof-bin/src/ch64.rs deleted file mode 100644 index dfb419bd..00000000 --- a/pyroscope/pprof-bin/src/ch64.rs +++ /dev/null @@ -1,177 +0,0 @@ -pub fn read_uint64_le(bytes: &[u8]) -> u64 { - let mut res: u64 = 0; - for i in 0..8 { - res |= (bytes[i] as u64) << (i * 8); - } - res -} - -const K_MUL: u64 = 0x9ddfea08eb382d69; - -pub fn hash_128_to_64(l: u64, h: u64) -> u64 { - let mut a = (l ^ h).wrapping_mul(K_MUL); - a ^= a >> 47; - let mut b = (h ^ a).wrapping_mul(K_MUL); - b ^= b >> 47; - b = b.wrapping_mul(K_MUL); - b -} - -const K0: u64 = 0xc3a5c85c97cb3127; -const K2: u64 = 0x9ae16a3b2f90404f; -const K1: u64 = 0xb492b66fbe98f273; -const K3: u64 = 0xc949d7c7509e6557; -fn ch16(u: u64, v: u64) -> u64 { - hash_128_to_64(u, v) -} - -fn rot64(val: u64, shift: usize) -> u64 { - if shift == 0 { - return val; - } - return (val >> shift) | val << (64 - shift); -} - -fn shift_mix(val: u64) -> u64 { - return val ^ (val >> 47); -} - -fn hash16(u: u64, v: u64) -> u64 { - hash_128_to_64(u, v) -} - -fn fetch32(p: &[u8]) -> u32 { - let mut res: u32 = 0; - for i in 0..4 { - res |= (p[i] as u32) << (i * 8); - } - res -} - -fn ch33to64(s: &[u8], length: usize) -> u64 { - let mut z = read_uint64_le(&s[24..]); - let mut a = - read_uint64_le(&s) + (length as u64 + read_uint64_le(&s[length - 16..])).wrapping_mul(K0); - let mut b = rot64(a + z, 52); - let mut c = rot64(a, 37); - a += read_uint64_le(&s[8..]); - c += rot64(a, 7); - a += read_uint64_le(&s[16..]); - let vf = a + z; - let vs = b + rot64(a, 31) + c; - - a = read_uint64_le(&s[16..]) + read_uint64_le(&s[length - 32..]); - z = read_uint64_le(&s[length - 8..]); - b = rot64(a + z, 52); - c = rot64(a, 37); - a += read_uint64_le(&s[length - 24..]); - c += rot64(a, 7); - a += read_uint64_le(&s[length - 16..]); - - let wf = a + z; - let ws = b + rot64(a, 31) + c; - let r = shift_mix((vf + ws).wrapping_mul(K2) + (wf + vs).wrapping_mul(K0)); - return shift_mix(r.wrapping_mul(K0) + vs).wrapping_mul(K2); -} - -fn ch17to32(s: &[u8], length: usize) -> u64 { - let a = read_uint64_le(s).wrapping_mul(K1); - let b = read_uint64_le(&s[8..]); - let c = read_uint64_le(&s[length - 8..]).wrapping_mul(K2); - let d = read_uint64_le(&s[length - 16..]).wrapping_mul(K0); - return hash16( - rot64(a - b, 43) + rot64(c, 30) + d, - a + rot64(b ^ K3, 20) - c + (length as u64), - ); -} - -fn ch0to16(s: &[u8], length: usize) -> u64 { - if length > 8 { - let a = read_uint64_le(s); - let b = read_uint64_le(&s[length - 8..]); - return ch16(a, rot64(b + (length as u64), length)) ^ b; - } - if length >= 4 { - let a = fetch32(s) as u64; - return ch16((length as u64) + (a << 3), fetch32(&s[length - 4..]) as u64); - } - if length > 0 { - let a = s[0]; - let b = s[length >> 1]; - let c = s[length - 1]; - let y = (a as u32) + ((b as u32) << 8); - let z = (length as u32) + ((c as u32) << 2); - return shift_mix((y as u64).wrapping_mul(K2) ^ (z as u64).wrapping_mul(K3)) - .wrapping_mul(K2); - } - return K2; -} - -fn weak_hash32_seeds(w: u64, x: u64, y: u64, z: u64, _a: u64, _b: u64) -> (u64, u64) { - let mut a = _a + w; - let mut b = rot64(_b + a + z, 21); - let c = a; - a += x; - a += y; - b += rot64(a, 44); - return (a + z, b + c); -} - -// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. -fn weak_hash32_seeds_byte(s: &[u8], a: u64, b: u64) -> (u64, u64) { - _ = s[31]; - return weak_hash32_seeds( - read_uint64_le(&s[0..0 + 8]), - read_uint64_le(&s[8..8 + 8]), - read_uint64_le(&s[16..16 + 8]), - read_uint64_le(&s[24..24 + 8]), - a, - b, - ); -} - -fn nearest_multiple_64(b: &[u8]) -> usize { - return ((b.len()) - 1) & !63; -} - -// CH64 returns ClickHouse version of Hash64. -pub fn city_hash_64(s: &[u8]) -> u64 { - let length = s.len(); - if length <= 16 { - return ch0to16(s, length); - } - if length <= 32 { - return ch17to32(s, length); - } - if length <= 64 { - return ch33to64(s, length); - } - - let x = read_uint64_le(s); - let y = read_uint64_le(&s[length - 16..]) ^ K1; - let mut z = read_uint64_le(&s[length - 56..]) ^ K0; - - let mut v = weak_hash32_seeds_byte(&s[length - 64..], length as u64, y); - let mut w = weak_hash32_seeds_byte(&s[length - 32..], (length as u64).wrapping_mul(K1), K0); - z += shift_mix(v.1).wrapping_mul(K1); - let mut x = rot64(z + x, 39).wrapping_mul(K1); - let mut y = rot64(y, 33).wrapping_mul(K1); - // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. - let mut _s = &s[..nearest_multiple_64(s)]; - while _s.len() > 0 { - x = rot64(x + y + v.0 + read_uint64_le(&s[16..]), 37).wrapping_mul(K1); - y = rot64(y + v.1 + read_uint64_le(&s[48..]), 42).wrapping_mul(K1); - x ^= w.1; - y ^= v.0; - - z = rot64(z ^ w.0, 33); - v = weak_hash32_seeds_byte(s, v.1.wrapping_mul(K1), x + w.0); - w = weak_hash32_seeds_byte(&s[32..], z + w.1, y); - (z, x) = (x, z); - _s = &_s[64..]; - } - return ch16( - ch16(v.0, w.0) + shift_mix(y).wrapping_mul(K1) + z, - ch16(v.1, w.1) + x, - ); -} diff --git a/pyroscope/pprof-bin/src/lib.rs b/pyroscope/pprof-bin/src/lib.rs deleted file mode 100644 index a6f264f7..00000000 --- a/pyroscope/pprof-bin/src/lib.rs +++ /dev/null @@ -1,828 +0,0 @@ -#![allow(unused_assignments)] -mod ch64; -mod merge; -pub mod utest; - -use ch64::city_hash_64; -use ch64::read_uint64_le; -use lazy_static::lazy_static; -use pprof_pb::google::v1::Function; -use pprof_pb::google::v1::Location; -use pprof_pb::google::v1::Profile; -use pprof_pb::google::v1::Sample; -use pprof_pb::querier::v1::FlameGraph; -use pprof_pb::querier::v1::FlameGraphDiff; -use pprof_pb::querier::v1::Level; -use pprof_pb::querier::v1::SelectMergeStacktracesResponse; -use prost::Message; -use std::collections::{HashMap, HashSet, VecDeque}; -use std::io::Read; -use std::panic; -use std::sync::Arc; -use std::sync::Mutex; -use std::vec::Vec; -use wasm_bindgen::prelude::*; - -pub mod pprof_pb { - - pub mod google { - pub mod v1 { - include!(concat!(env!("OUT_DIR"), "/google.v1.rs")); - } - } - pub mod types { - pub mod v1 { - include!(concat!(env!("OUT_DIR"), "/types.v1.rs")); - } - } - pub mod querier { - pub mod v1 { - include!(concat!(env!("OUT_DIR"), "/querier.v1.rs")); - } - } -} - -struct TreeNodeV2 { - //parent_id: u64, - fn_id: u64, - node_id: u64, - slf: Vec<i64>, - total: Vec<i64>, -} - -impl TreeNodeV2 { - pub fn clone(&self) -> TreeNodeV2 { - TreeNodeV2 { - fn_id: self.fn_id, - node_id: self.node_id, - slf: self.slf.clone(), - total: self.total.clone(), - } - } - pub fn set_total_and_self(&self, slf: Vec<i64>, total: Vec<i64>) -> TreeNodeV2 { - let mut res = self.clone(); - res.slf = slf; - res.total = total; - return res; - } -} - -struct Tree { - names: Vec<String>, - names_map: HashMap<u64, usize>, - nodes: HashMap<u64, Vec<Arc<TreeNodeV2>>>, - sample_types: Vec<String>, - max_self: Vec<i64>, - nodes_num: i32, - pprof: Profile, -} - -impl Tree { - pub fn total(&self) -> Vec<i64> { - if let Some(children) = self.nodes.get(&0) { - let mut total = vec![0; children[0].total.len()]; - for child in children.iter() { - for (t, &child_total) in total.iter_mut().zip(&child.total) { - *t += child_total; - } - } - total - } else { - Vec::new() - } - } - - pub fn add_name(&mut self, name: String, name_hash: u64) { - if let std::collections::hash_map::Entry::Vacant(entry) = self.names_map.entry(name_hash) { - self.names.push(name); - entry.insert(self.names.len() - 1); - } - } -} - -fn find_node(id: u64, nodes: &[Arc<TreeNodeV2>]) -> Option<usize> { - nodes.iter().position(|node| node.node_id == id) -} - -fn get_node_id(parent_id: u64, name_hash: u64, level: u16) -> u64 { - let mut node_bytes = [0u8; 16]; - node_bytes[..8].copy_from_slice(&parent_id.to_le_bytes()); - node_bytes[8..].copy_from_slice(&name_hash.to_le_bytes()); - - let adjusted_level = level.min(511); - (city_hash_64(&node_bytes) >> 9) | ((adjusted_level as u64) << 55) -} - -struct MergeTotalsProcessor { - from_idx: Vec<Option<usize>>, -} - -impl MergeTotalsProcessor { - fn new(tree: &Tree, p: &Profile) -> MergeTotalsProcessor { - let from_idx: Vec<Option<usize>> = tree - .sample_types - .iter() - .map(|sample_type_to| { - p.sample_type.iter().position(|sample_type| { - let sample_type_from = format!( - "{}:{}", - p.string_table[sample_type.r#type as usize], - p.string_table[sample_type.unit as usize] - ); - sample_type_from == *sample_type_to - }) - }) - .collect(); - - MergeTotalsProcessor { from_idx } - } - - fn merge_totals( - &self, - node: Arc<TreeNodeV2>, - max_self: &mut Vec<i64>, - sample: &Sample, - merge_self: bool, - ) -> TreeNodeV2 { - let mut res: TreeNodeV2 = TreeNodeV2 { - fn_id: node.fn_id, - node_id: node.node_id, - slf: vec![0; node.slf.len()], - total: vec![0; node.slf.len()], - }; - - for (i, opt_idx) in self.from_idx.iter().enumerate() { - if let Some(from_idx) = opt_idx { - res.total[i] += sample.value[*from_idx]; - if merge_self { - res.slf[i] += sample.value[*from_idx]; - if max_self[i] < node.slf[i] { - max_self[i] = node.slf[i]; - } - } - } - } - - res - } -} - -fn merge(tree: &mut Tree, p: &Profile) { - let functions: HashMap<u64, &Function> = p.function.iter().map(|f| (f.id, f)).collect(); - let locations: HashMap<u64, &Location> = p.location.iter().map(|l| (l.id, l)).collect(); - - let merge_processor = MergeTotalsProcessor::new(tree, p); - - for location in &p.location { - if let Some(function) = functions.get(&location.line[0].function_id) { - let line = &p.string_table[function.name as usize]; - let line_hash = city_hash_64(line.as_bytes()); - - if let std::collections::hash_map::Entry::Vacant(entry) = - tree.names_map.entry(line_hash) - { - tree.names.push(line.clone()); - entry.insert(tree.names.len() - 1); - } - } - } - - for sample in &p.sample { - let mut parent_id: u64 = 0; - - for (i, &location_id) in sample.location_id.iter().enumerate().rev() { - if let Some(location) = locations.get(&location_id) { - if let Some(function) = functions.get(&location.line[0].function_id) { - let name = &p.string_table[function.name as usize]; - let name_hash = city_hash_64(name.as_bytes()); - let node_id = - get_node_id(parent_id, name_hash, (sample.location_id.len() - i) as u16); - - let children = tree.nodes.entry(parent_id).or_insert_with(Vec::new); - - match find_node(node_id, children) { - Some(index) => { - if tree.nodes_num < 2_000_000 { - let updated_node = merge_processor.merge_totals( - children[index].clone(), - &mut tree.max_self, - sample, - i == 0, - ); - children[index] = Arc::new(updated_node); - tree.nodes_num += 1; - } - } - None => { - if tree.nodes_num < 2_000_000 { - let new_node = TreeNodeV2 { - fn_id: name_hash, - node_id, - slf: vec![0; tree.sample_types.len()], - total: vec![0; tree.sample_types.len()], - }; - - let new_node_arc = Arc::new(new_node); - let updated_node = merge_processor.merge_totals( - new_node_arc.clone(), - &mut tree.max_self, - sample, - i == 0, - ); - - children.push(Arc::new(updated_node)); - tree.nodes_num += 1; - } - } - } - - parent_id = node_id; - } - } - } - } -} - -fn read_uleb128(bytes: &[u8]) -> (usize, usize) { - let mut result = 0usize; - let mut shift = 0; - - for (index, &byte) in bytes.iter().enumerate() { - result |= ((byte & 0x7f) as usize) << shift; - shift += 7; - - if byte & 0x80 == 0 { - return (result, index + 1); - } - } - - (result, bytes.len()) -} - -fn bfs(t: &Tree, res: &mut Vec<Level>, sample_type: String) { - let sample_type_index = match t.sample_types.iter().position(|x| x == &sample_type) { - Some(index) => index, - None => return, - }; - - let empty_vec = Vec::new(); - let root_children = t.nodes.get(&0u64).unwrap_or(&empty_vec); - - let total: i64 = root_children - .iter() - .map(|child| child.total[sample_type_index]) - .sum(); - - res.push(Level { - values: vec![0, total, 0, 0], - }); - - let mut totals = vec![0; t.sample_types.len()]; - totals[sample_type_index] = total; - - let total_node = TreeNodeV2 { - slf: vec![0; t.sample_types.len()], - total: totals, - node_id: 0, - fn_id: 0, - }; - - let mut prepend_map: HashMap<u64, i64> = HashMap::new(); - let mut reviewed: HashSet<u64> = HashSet::new(); - - let mut current_level_nodes = vec![&total_node]; - - while !current_level_nodes.is_empty() { - let mut next_level_nodes = Vec::new(); - let mut prepend: i64 = 0; - let mut lvl = Level::default(); - - for parent in current_level_nodes { - prepend += *prepend_map.get(&parent.node_id).unwrap_or(&0); - - if let Some(children) = t.nodes.get(&parent.node_id) { - for child in children { - if !reviewed.insert(child.node_id) { - // Loop detected, exit early - return; - } - - prepend_map.insert(child.node_id, prepend); - next_level_nodes.push(child.as_ref()); - - lvl.values.extend_from_slice(&[ - prepend, - child.total[sample_type_index], - child.slf[sample_type_index], - *t.names_map.get(&child.fn_id).unwrap_or(&1) as i64, - ]); - - prepend = 0; - } - } else { - prepend += parent.total[sample_type_index]; - continue; - } - - prepend += parent.slf[sample_type_index]; - } - - res.push(lvl); - current_level_nodes = next_level_nodes; - } -} - -lazy_static! { - static ref CTX: Mutex<HashMap<u32, Mutex<Tree>>> = Mutex::new(HashMap::new()); -} - -fn upsert_tree(ctx: &mut HashMap<u32, Mutex<Tree>>, id: u32, sample_types: Vec<String>) { - if !ctx.contains_key(&id) { - let _len = sample_types.len().clone(); - ctx.insert( - id, - Mutex::new(Tree { - names: vec!["total".to_string(), "n/a".to_string()], - names_map: HashMap::new(), - nodes: HashMap::new(), - sample_types, - max_self: vec![0; _len], - nodes_num: 1, - pprof: Profile::default(), - }), - ); - } -} - -struct TrieReader { - bytes: Vec<u8>, - offs: usize, -} - -impl TrieReader { - fn new(bytes: &[u8]) -> TrieReader { - TrieReader { - bytes: bytes.to_vec(), - offs: 0, - } - } - - fn read_uint64_le(&mut self) -> u64 { - let res = read_uint64_le(&self.bytes[self.offs..]); - self.offs += 8; - res - } - - fn read_size(&mut self) -> usize { - let res = read_uleb128(&self.bytes[self.offs..]); - self.offs += res.1; - res.0.clone() - } - - fn read_string(&mut self) -> String { - let size = self.read_size(); - let string = String::from_utf8_lossy(&self.bytes[self.offs..self.offs + size]).to_string(); - self.offs += size; - string - } - - fn read_blob_vec(&mut self) -> Vec<&[u8]> { - let mut res = Vec::new(); - let size = self.read_size(); - for _ in 0..size { - let uleb = read_uleb128(&self.bytes[self.offs..]); - self.offs += uleb.1; - let _size = uleb.0; - let string = &self.bytes[self.offs..self.offs + _size]; - self.offs += _size; - res.push(string); - } - res - } - fn read_blob(&mut self) -> &[u8] { - let size = self.read_size(); - let string = &self.bytes[self.offs..self.offs + size]; - self.offs += size; - string - } - fn read_blob_list(&mut self) -> Vec<&[u8]> { - let mut res = Vec::new(); - while self.offs < self.bytes.len() { - let uleb = read_uleb128(&self.bytes[self.offs..]); - self.offs += uleb.1; - let _size = uleb.0; - let string = &self.bytes[self.offs..self.offs + _size]; - self.offs += _size; - res.push(string); - } - res - } -} - -fn merge_trie(tree: &mut Tree, bytes: &[u8], sample_type: &str) { - let sample_type_index = match tree.sample_types.iter().position(|x| x == sample_type) { - Some(index) => index, - None => return, - }; - - let mut reader = TrieReader::new(bytes); - - for _ in 0..reader.read_size() { - let id = reader.read_uint64_le(); - let func = reader.read_string(); - if tree.names_map.len() < 2_000_000 { - if !tree.names_map.contains_key(&id) { - tree.names.push(func); - tree.names_map.insert(id, tree.names.len() - 1); - } - } - } - - for _ in 0..reader.read_size() { - let parent_id = reader.read_uint64_le(); - let fn_id = reader.read_uint64_le(); - let node_id = reader.read_uint64_le(); - let slf_value = reader.read_uint64_le() as i64; - let total_value = reader.read_uint64_le() as i64; - - if tree.max_self[sample_type_index] < slf_value { - tree.max_self[sample_type_index] = slf_value; - } - - let mut slf = vec![0; tree.sample_types.len()]; - slf[sample_type_index] = slf_value; - - let mut total = vec![0; tree.sample_types.len()]; - total[sample_type_index] = total_value; - - if let Some(children) = tree.nodes.get_mut(&parent_id) { - if let Some(pos) = find_node(node_id, children) { - let node_arc = &children[pos]; - let mut node = node_arc.as_ref().clone(); - - node.slf[sample_type_index] += slf_value; - node.total[sample_type_index] += total_value; - - children[pos] = Arc::new(node); - continue; - } - } - - if tree.nodes_num >= 2_000_000 { - return; - } - - let children = tree.nodes.entry(parent_id).or_insert_with(Vec::new); - children.push(Arc::new(TreeNodeV2 { - fn_id, - node_id, - slf, - total, - })); - - tree.nodes_num += 1; - } -} - -fn assert_positive(t: &Tree) -> bool { - for n in t.nodes.keys() { - for _n in 0..t.nodes.get(&n).unwrap().len() { - for __n in 0..t.nodes.get(&n).unwrap()[_n].slf.len() { - if t.nodes.get(&n).unwrap()[_n].slf[__n] < 0 { - return false; - } - } - } - } - true -} - -#[wasm_bindgen] -pub fn merge_prof(id: u32, bytes: &[u8], sample_type: String) { - let p = panic::catch_unwind(|| { - let mut ctx = CTX.lock().unwrap(); - upsert_tree(&mut ctx, id, vec![sample_type]); - let mut tree = ctx.get_mut(&id).unwrap().lock().unwrap(); - let prof = Profile::decode(bytes).unwrap(); - merge(&mut tree, &prof); - }); - match p { - Ok(_) => {} - Err(err) => panic!("{:?}", err), - } -} - -#[wasm_bindgen] -pub fn merge_tree(id: u32, bytes: &[u8], sample_type: String) { - let result = panic::catch_unwind(|| { - let mut ctx = CTX.lock().unwrap(); - upsert_tree(&mut ctx, id, vec![sample_type.clone()]); - let mut tree = ctx.get_mut(&id).unwrap().lock().unwrap(); - merge_trie(&mut tree, bytes, &sample_type); - 0 - }); - match result { - Ok(_) => {} - Err(err) => panic!("{:?}", err), - } -} - -#[wasm_bindgen] -pub fn diff_tree(id1: u32, id2: u32, sample_type: String) -> Vec<u8> { - let mut ctx = CTX.lock().unwrap(); - upsert_tree(&mut ctx, id1, vec![sample_type.clone()]); - upsert_tree(&mut ctx, id2, vec![sample_type.clone()]); - - let mut t1 = ctx.get(&id1).unwrap().lock().unwrap(); - let mut t2 = ctx.get(&id2).unwrap().lock().unwrap(); - - assert_tree_positive(&t1, "Tree 1"); - assert_tree_positive(&t2, "Tree 2"); - - synchronize_names(&mut t1, &mut t2); - merge_nodes(&mut t1, &mut t2); - - let flame_graph_diff = compute_flame_graph_diff(&t1, &t2); - - flame_graph_diff.encode_to_vec() -} - -fn assert_tree_positive(tree: &Tree, tree_name: &str) { - if !assert_positive(tree) { - panic!("{} is not positive", tree_name); - } -} - -fn synchronize_names(t1: &mut Tree, t2: &mut Tree) { - let mut names_to_add_to_t2 = vec![]; - for (&id, &idx) in &t1.names_map { - if !t2.names_map.contains_key(&id) { - names_to_add_to_t2.push((id, t1.names[idx].clone())); - } - } - - for (id, name) in names_to_add_to_t2 { - let idx = t2.names.len(); - t2.names.push(name); - t2.names_map.insert(id, idx); - } - - let mut names_to_add_to_t1 = vec![]; - for (&id, &idx) in &t2.names_map { - if !t1.names_map.contains_key(&id) { - names_to_add_to_t1.push((id, t2.names[idx].clone())); - } - } - - for (id, name) in names_to_add_to_t1 { - let idx = t1.names.len(); - t1.names.push(name); - t1.names_map.insert(id, idx); - } -} - -fn merge_nodes(t1: &mut Tree, t2: &mut Tree) { - let mut keys: HashSet<u64> = HashSet::new(); - keys.extend(t1.nodes.keys()); - keys.extend(t2.nodes.keys()); - - for key in keys { - let t1_children = t1.nodes.entry(key).or_insert_with(Vec::new); - let t2_children = t2.nodes.entry(key).or_insert_with(Vec::new); - - t1_children.sort_by_key(|n| n.node_id); - t2_children.sort_by_key(|n| n.node_id); - - let (new_t1_nodes, new_t2_nodes) = merge_children(t1_children, t2_children); - t1.nodes.insert(key, new_t1_nodes); - t2.nodes.insert(key, new_t2_nodes); - } -} - -fn merge_children( - t1_nodes: &[Arc<TreeNodeV2>], - t2_nodes: &[Arc<TreeNodeV2>], -) -> (Vec<Arc<TreeNodeV2>>, Vec<Arc<TreeNodeV2>>) { - let mut new_t1_nodes = Vec::new(); - let mut new_t2_nodes = Vec::new(); - let mut i = 0; - let mut j = 0; - - while i < t1_nodes.len() && j < t2_nodes.len() { - if t1_nodes[i].node_id == t2_nodes[j].node_id { - new_t1_nodes.push(t1_nodes[i].clone()); - new_t2_nodes.push(t2_nodes[j].clone()); - i += 1; - j += 1; - } else if t1_nodes[i].node_id < t2_nodes[j].node_id { - new_t1_nodes.push(t1_nodes[i].clone()); - new_t2_nodes.push(create_empty_node(&t1_nodes[i])); - i += 1; - } else { - new_t2_nodes.push(t2_nodes[j].clone()); - new_t1_nodes.push(create_empty_node(&t2_nodes[j])); - j += 1; - } - } - - while i < t1_nodes.len() { - new_t1_nodes.push(t1_nodes[i].clone()); - new_t2_nodes.push(create_empty_node(&t1_nodes[i])); - i += 1; - } - - while j < t2_nodes.len() { - new_t2_nodes.push(t2_nodes[j].clone()); - new_t1_nodes.push(create_empty_node(&t2_nodes[j])); - j += 1; - } - - (new_t1_nodes, new_t2_nodes) -} - -fn create_empty_node(node: &Arc<TreeNodeV2>) -> Arc<TreeNodeV2> { - Arc::new(TreeNodeV2 { - node_id: node.node_id, - fn_id: node.fn_id, - slf: vec![0], - total: vec![0], - }) -} - -fn compute_flame_graph_diff(t1: &Tree, t2: &Tree) -> FlameGraphDiff { - let mut res = FlameGraphDiff::default(); - res.left_ticks = t1.total()[0]; - res.right_ticks = t2.total()[0]; - res.total = res.left_ticks + res.right_ticks; - - let mut left_nodes: VecDeque<Arc<TreeNodeV2>> = VecDeque::new(); - left_nodes.push_back(Arc::new(TreeNodeV2 { - fn_id: 0, - node_id: 0, - slf: vec![0], - total: vec![res.left_ticks], - })); - - let mut right_nodes: VecDeque<Arc<TreeNodeV2>> = VecDeque::new(); - right_nodes.push_back(Arc::new(TreeNodeV2 { - fn_id: 0, - node_id: 0, - slf: vec![0], - total: vec![res.right_ticks], - })); - - let mut levels = vec![0]; - let mut x_left_offsets: VecDeque<i64> = VecDeque::new(); - x_left_offsets.push_back(0); - let mut x_right_offsets = VecDeque::new(); - x_right_offsets.push_back(0); - let mut name_location_cache: HashMap<String, i64> = HashMap::new(); - - while let (Some(left), Some(right)) = - (left_nodes.pop_back(), right_nodes.pop_back()) { - let mut x_left_offset = x_left_offsets.pop_back().unwrap().clone(); - let mut x_right_offset = x_right_offsets.pop_back().unwrap().clone(); - let level = levels.pop().unwrap(); - - let name = if left.fn_id == 0 { - "total".to_string() - } else { - t1.names[*t1.names_map.get(&left.fn_id).unwrap()].clone() - }; - - let name_idx = *name_location_cache.entry(name.clone()).or_insert_with(|| { - res.names.push(name); - (res.names.len() - 1) as i64 - }); - - if res.levels.len() <= level { - res.levels.push(Level::default()); - } - - if res.max_self < left.slf[0] { - res.max_self = left.slf[0]; - } - if res.max_self < right.slf[0] { - res.max_self = right.slf[0]; - } - - res.levels[level].values.extend_from_slice(&[ - x_left_offset, - left.total[0], - left.slf[0], - x_right_offset, - right.total[0], - right.slf[0], - name_idx, - ]); - - if let Some(children_left) = t1.nodes.get(&left.node_id) { - let empty_vec = Vec::new(); - let children_right = t2.nodes.get(&right.node_id).unwrap_or(&empty_vec); - for (child_left, child_right) in children_left.iter().zip(children_right.iter()) { - left_nodes.push_front(child_left.clone()); - right_nodes.push_front(child_right.clone()); - x_left_offsets.push_front(x_left_offset.clone()); - x_right_offsets.push_front(x_right_offset.clone()); - x_left_offset += child_left.total[0].clone(); - x_right_offset += child_right.total[0].clone(); - levels.insert(0,level + 1); - } - } - } - - for i in 0..res.levels.len() { - let mut j = 0; - let mut prev0 = 0i64; - let mut prev3 = 0i64; - while j < res.levels[i].values.len() { - res.levels[i].values[j] -= prev0; - prev0 += res.levels[i].values[j] + res.levels[i].values[j+1]; - res.levels[i].values[j+3] -= prev3; - prev3 += res.levels[i].values[j+3] + res.levels[i].values[j+4]; - j += 7; - } - } - - res -} - -#[wasm_bindgen] -pub fn export_tree(id: u32, sample_type: String) -> Vec<u8> { - let p = panic::catch_unwind(|| { - let mut ctx = CTX.lock().unwrap(); - let mut res = SelectMergeStacktracesResponse::default(); - upsert_tree(&mut ctx, id, vec![sample_type.clone()]); - let tree = ctx.get_mut(&id).unwrap().lock().unwrap(); - let mut fg = FlameGraph::default(); - fg.names = tree.names.clone(); - fg.max_self = tree.max_self[0 /* TODO */]; - fg.total = 0; - let mut root_children: &Vec<Arc<TreeNodeV2>> = &vec![]; - if tree.nodes.contains_key(&(0u64)) { - root_children = tree.nodes.get(&(0u64)).unwrap(); - } - for n in root_children.iter() { - fg.total += n.total[0 /*TODO*/] as i64; - } - bfs(&tree, &mut fg.levels, sample_type.clone()); - res.flamegraph = Some(fg); - return res.encode_to_vec(); - }); - match p { - Ok(res) => return res, - Err(err) => panic!("{:?}", err), - } -} - -#[wasm_bindgen] -pub fn merge_trees_pprof(id: u32, payload: &[u8]) { - let p = panic::catch_unwind(|| { - let mut ctx = CTX.lock().unwrap(); - upsert_tree(&mut ctx, id, vec![]); - let mut tree = ctx.get_mut(&id).unwrap().lock().unwrap(); - let mut reader = TrieReader::new(payload); - let bin_profs = reader.read_blob_list(); - let mut merger = merge::ProfileMerge::new(); - merger.merge(&mut tree.pprof); - for bin_prof in bin_profs { - if bin_prof.len() >= 2 && bin_prof[0] == 0x1f && bin_prof[1] == 0x8b { - let mut decompressed = Vec::new(); - let mut decoder = flate2::read::GzDecoder::new(&bin_prof[..]); - decoder.read_to_end(&mut decompressed).unwrap(); - let mut prof = Profile::decode(std::io::Cursor::new(decompressed)).unwrap(); - merger.merge(&mut prof); - } else { - let mut prof = Profile::decode(bin_prof).unwrap(); - merger.merge(&mut prof); - } - } - let res = merger.profile(); - tree.pprof = res; - }); - match p { - Ok(_) => {} - Err(err) => panic!("{:?}", err), - } -} - -#[wasm_bindgen] -pub fn export_trees_pprof(id: u32) -> Vec<u8> { - let mut ctx = CTX.lock().unwrap(); - upsert_tree(&mut ctx, id, vec![]); - let tree = ctx.get_mut(&id).unwrap().lock().unwrap(); - tree.pprof.encode_to_vec() -} - -#[wasm_bindgen] -pub fn drop_tree(id: u32) { - let mut ctx = CTX.lock().unwrap(); - if ctx.contains_key(&id) { - ctx.remove(&id); - } -} - -#[wasm_bindgen] -pub fn init_panic_hook() { - console_error_panic_hook::set_once(); -} diff --git a/pyroscope/pprof-bin/src/merge.rs b/pyroscope/pprof-bin/src/merge.rs deleted file mode 100644 index 7425f506..00000000 --- a/pyroscope/pprof-bin/src/merge.rs +++ /dev/null @@ -1,679 +0,0 @@ -use crate::ch64::city_hash_64; -use crate::pprof_pb::google::v1::Function; -use crate::pprof_pb::google::v1::Line; -use crate::pprof_pb::google::v1::Location; -use crate::pprof_pb::google::v1::Mapping; -use crate::pprof_pb::google::v1::Sample; -use crate::pprof_pb::google::v1::ValueType; -use crate::pprof_pb::google::v1::{Label, Profile}; -use bytemuck; -use std::cmp::Ordering; -use std::collections::HashMap; -use std::hash::{Hash, Hasher}; - - -pub struct ProfileMerge { - prof: Option<Profile>, - tmp: Vec<u32>, - - string_table: Option<RewriteTable<String, String, String>>, - function_table: Option<RewriteTable<FunctionKey, Function, Function>>, - mapping_table: Option<RewriteTable<MappingKey, Mapping, Mapping>>, - location_table: Option<RewriteTable<LocationKey, Location, Location>>, - sample_table: Option<RewriteTable<SampleKey, Sample, Sample>>, -} - -impl ProfileMerge { - pub(crate) fn new() -> ProfileMerge { - ProfileMerge { - prof: Option::None, - tmp: Vec::new(), - - string_table: Option::None, - function_table: Option::None, - mapping_table: Option::None, - location_table: Option::None, - sample_table: Option::None, - } - } - pub fn merge(&mut self, p: &mut Profile) { - if p.sample.len() == 0 || p.string_table.len() < 2 { - return; - } - - sanitize_profile(&mut Some(p)); - - let mut initial = false; - if self.prof.is_none() { - self.init(p); - initial = true; - } - - self.tmp.resize(p.string_table.len(), 0); - self.string_table - .as_mut() - .unwrap() - .index(&mut self.tmp, &p.string_table); - - rewrite_strings(p, &mut self.tmp); - if initial { - rewrite_strings(self.prof.as_mut().unwrap(), &mut self.tmp) - } - - combine_headers(self.prof.as_mut().unwrap(), p); - - self.tmp.resize(p.function.len(), 0); - self.function_table - .as_mut() - .unwrap() - .index(&mut self.tmp, &p.function); - rewrite_functions(p, &mut self.tmp); - - self.tmp.resize(p.mapping.len(), 0); - self.mapping_table - .as_mut() - .unwrap() - .index(&mut self.tmp, &p.mapping); - rewrite_mappings(p, &mut self.tmp); - - self.tmp.resize(p.location.len(), 0); - self.location_table - .as_mut() - .unwrap() - .index(&mut self.tmp, &p.location); - rewrite_locations(p, &mut self.tmp); - - self.tmp.resize(p.sample.len(), 0); - self.sample_table - .as_mut() - .unwrap() - .index(&mut self.tmp, &p.sample); - - for i in 0..self.tmp.len() { - let idx = self.tmp[i]; - let dst = &mut self.sample_table.as_mut().unwrap().s[idx as usize].value; - let src = p.sample[i as usize].value.clone(); - for j in 0..src.len() { - dst[j] += src[j]; - } - } - } - - fn init(&mut self, p: &mut Profile) { - let factor = 2; - self.string_table = Some(RewriteTable::new( - factor * p.string_table.len(), - |s| s.clone(), - |s| s.clone(), - )); - self.function_table = Some(RewriteTable::new( - factor * p.function.len(), - FunctionKey::get, - |s| s.clone(), - )); - self.mapping_table = Some(RewriteTable::new( - factor * p.mapping.len(), - MappingKey::get, - |s| s.clone(), - )); - self.location_table = Some(RewriteTable::new( - factor * p.location.len(), - LocationKey::get, - |s| s.clone(), - )); - self.sample_table = Some(RewriteTable::new( - factor * p.sample.len(), - SampleKey::get, - |s| s.clone(), - )); - let mut _prof = Profile::default(); - _prof.sample_type = vec![]; - - _prof.drop_frames = p.drop_frames.clone(); - _prof.keep_frames = p.keep_frames.clone(); - _prof.time_nanos = p.time_nanos.clone(); - _prof.period_type = p.period_type.clone(); - _prof.period = p.period.clone(); - _prof.default_sample_type = p.default_sample_type.clone(); - for s in 0..p.sample_type.len() { - _prof.sample_type.push(p.sample_type[s].clone()); - } - self.prof = Some(_prof); - } - - pub fn profile(&mut self) -> Profile { - if self.prof.is_none() { - return Profile::default(); - } - let mut p = self.prof.as_mut().unwrap().clone(); - p.sample = self.sample_table.as_mut().unwrap().values().clone(); - p.location = self.location_table.as_mut().unwrap().values().clone(); - p.function = self.function_table.as_mut().unwrap().values().clone(); - p.mapping = self.mapping_table.as_mut().unwrap().values().clone(); - p.string_table = self.string_table.as_mut().unwrap().values().clone(); - for i in 0..p.location.len() { - p.location[i].id = i as u64 + 1; - } - for i in 0..p.function.len() { - p.function[i].id = i as u64 + 1; - } - for i in 0..p.mapping.len() { - p.mapping[i].id = i as u64 + 1; - } - return p; - } - -} - -fn rewrite_strings(p: &mut Profile, n: &Vec<u32>) { - for i in 0..p.sample_type.len() { - let t = &mut p.sample_type[i]; - if t.unit != 0 { - t.unit = n[t.unit as usize] as i64; - } - if t.r#type != 0 { - t.r#type = n[t.r#type as usize] as i64; - } - } - for i in 0..p.sample.len() { - let s = &mut p.sample[i]; - for j in 0..s.label.len() { - let l = &mut s.label[j]; - l.key = n[l.key as usize] as i64; - l.str = n[l.str as usize] as i64; - } - } - - for i in 0..p.mapping.len() { - let m = &mut p.mapping[i]; - m.filename = n[m.filename as usize] as i64; - m.build_id = n[m.build_id as usize] as i64; - } - - for i in 0..p.function.len() { - let f = &mut p.function[i]; - f.name = n[f.name as usize] as i64; - f.filename = n[f.filename as usize] as i64; - f.system_name = n[f.system_name as usize] as i64; - } - p.drop_frames = n[p.drop_frames as usize] as i64; - p.keep_frames = n[p.keep_frames as usize] as i64; - if !p.period_type.is_none() { - if p.period_type.as_mut().unwrap().r#type != 0 { - p.period_type.as_mut().unwrap().r#type = - n[p.period_type.as_mut().unwrap().r#type as usize] as i64; - } - if p.period_type.as_mut().unwrap().unit != 0 { - p.period_type.as_mut().unwrap().unit = - n[p.period_type.as_mut().unwrap().unit as usize] as i64; - } - } - - for i in 0..p.comment.len() { - let x = p.comment[i]; - p.comment[i] = n[x as usize] as i64; - } - p.default_sample_type = n[p.default_sample_type as usize] as i64; -} - -fn rewrite_functions(p: &mut Profile, n: &Vec<u32>) { - for i in 0..p.location.len() { - let loc = &mut p.location[i]; - for j in 0..loc.line.len() { - let line = &mut loc.line[j]; - if line.function_id > 0 { - line.function_id = n[line.function_id as usize - 1] as u64 + 1; - } - } - } -} - -fn rewrite_mappings(p: &mut Profile, n: &mut Vec<u32>) { - for i in 0..p.location.len() { - let loc = &mut p.location[i]; - if loc.mapping_id > 0 { - loc.mapping_id = n[loc.mapping_id as usize - 1] as u64 + 1; - } - } -} - -fn rewrite_locations(p: &mut Profile, n: &mut Vec<u32>) { - for i in 0..p.sample.len() { - let s = &mut p.sample[i]; - for j in 0..s.location_id.len() { - if s.location_id[j] > 0 { - s.location_id[j] = n[s.location_id[j] as usize - 1] as u64 + 1; - } - } - } -} - -fn sanitize_profile(_p: &mut Option<&mut Profile>) { - if _p.is_none() { - return; - } - let p = _p.as_mut().unwrap(); - let mut ms = p.string_table.len() as i64; - let mut z: i64 = -1; - for i in 0..p.string_table.len() { - let s = &p.string_table[i]; - if s == "" { - z = i as i64; - break; - } - } - if z == -1 { - z = ms; - p.string_table.push("".to_string()); - ms += 1; - } - let tmp = p.string_table[0].clone(); - p.string_table[0] = p.string_table[z as usize].clone(); - p.string_table[z as usize] = tmp; - - let str = |i: i64| -> i64 { - if i == 0 && z > 0 { - return z; - } - if i == z || i >= ms || i < 0 { - return 0; - } - return i; - }; - p.sample_type = remove_in_place(&mut p.sample_type, &mut |x, _| -> bool { - x.r#type = str(x.r#type); - x.unit = str(x.unit); - false - }); - - if !p.period_type.is_none() { - p.period_type.as_mut().unwrap().r#type = str(p.period_type.as_mut().unwrap().r#type); - p.period_type.as_mut().unwrap().unit = str(p.period_type.as_mut().unwrap().unit); - } - - p.default_sample_type = str(p.default_sample_type); - p.drop_frames = str(p.drop_frames); - p.keep_frames = str(p.keep_frames); - for i in 0..p.comment.len() { - p.comment[i] = str(p.comment[i]); - } - - let mut t: HashMap<u64, u64> = HashMap::new(); - let mut j: u64 = 1; - p.mapping = remove_in_place(&mut p.mapping, &mut |x, _| -> bool { - x.build_id = str(x.build_id); - x.filename = str(x.filename); - t.insert(x.id, j); - x.id = j; - j += 1; - false - }); - - let mut mapping: Option<Mapping> = Option::None; - let p_mapping = &mut p.mapping; - p.location = remove_in_place(&mut p.location, &mut |x, _| -> bool { - if x.mapping_id == 0 { - if mapping.is_none() { - let mut _mapping = Mapping::default(); - _mapping.id = p_mapping.len() as u64 + 1; - mapping = Some(_mapping.clone()); - p_mapping.push(_mapping); - } - x.mapping_id = mapping.as_ref().unwrap().id; - return false; - } - x.mapping_id = t[&x.mapping_id]; - return x.mapping_id == 0; - }); - - t.clear(); - - j = 1; - p.function = remove_in_place(&mut p.function, &mut |x, _| -> bool { - x.name = str(x.name); - x.system_name = str(x.system_name); - x.filename = str(x.filename); - t.insert(x.id, j); - x.id = j; - j += 1; - false - }); - - p.location = remove_in_place(&mut p.location, &mut |x, _| -> bool { - for i in 0..x.line.len() { - let line = &mut x.line[i]; - line.function_id = t[&line.function_id]; - if line.function_id == 0 { - return true; - } - } - return false; - }); - - t.clear(); - j = 1; - for i in 0..p.location.len() { - let x = &mut p.location[i]; - t.insert(x.id, j); - x.id = j; - j += 1; - } - - let vs = p.sample_type.len(); - p.sample = remove_in_place(&mut p.sample, &mut |x, _| -> bool { - if x.value.len() != vs { - return true; - } - for i in 0..x.location_id.len() { - x.location_id[i] = t[&x.location_id[i]]; - if x.location_id[i] == 0 { - return true; - } - } - for i in 0..x.label.len() { - let l = &mut x.label[i]; - l.key = str(l.key); - l.str = str(l.str); - l.num_unit = str(l.num_unit); - } - false - }); -} - -fn remove_in_place<T: Clone, F: FnMut(&mut T, i64) -> bool>( - collection: &mut Vec<T>, - predicate: &mut F, -) -> Vec<T> { - let mut i: usize = 0; - for j in 0..collection.len() { - if !predicate(&mut collection[j], j as i64) { - let tmp = collection[i].clone(); - collection[i] = collection[j].clone(); - collection[j] = tmp; - i += 1; - } - } - return collection[..i].to_vec(); - /* - i := 0 - for j, x := range collection { - if !predicate(x, j) { - collection[j], collection[i] = collection[i], collection[j] - i++ - } - } - return collection[:i] - - */ -} - -fn combine_headers(a: &mut Profile, b: &Profile) { - compatible(a, b); - if a.time_nanos == 0 || b.time_nanos < a.time_nanos { - a.time_nanos = b.time_nanos - } - a.duration_nanos += b.duration_nanos; - if a.period == 0 || a.period < b.period { - a.period = b.period - } - if a.default_sample_type == 0 { - a.default_sample_type = b.default_sample_type - } -} -fn compatible(a: &Profile, b: &Profile) { - if !equal_value_type(&a.period_type, &b.period_type) { - panic!( - "incompatible period types {:?} and {:?}", - a.period_type, b.period_type - ); - } - if b.sample_type.len() != a.sample_type.len() { - panic!( - "incompatible sample types {:?} and {:?}", - a.sample_type, b.sample_type - ); - } - for i in 0..a.sample_type.len() { - if !equal_value_type( - &Some(a.sample_type[i].clone()), - &Some(b.sample_type[i].clone()), - ) { - panic!( - "incompatible sample types {:?} and {:?}", - a.sample_type, b.sample_type - ); - } - } -} - -fn equal_value_type(st1: &Option<ValueType>, st2: &Option<ValueType>) -> bool { - if st1.is_none() || st2.is_none() { - return false; - } - return st1.as_ref().unwrap().r#type == st2.as_ref().unwrap().r#type - && st1.as_ref().unwrap().unit == st2.as_ref().unwrap().unit; -} - -struct FunctionKey { - start_line: u32, - name: u32, - system_name: u32, - file_name: u32, -} - -impl FunctionKey { - fn get(f: &Function) -> FunctionKey { - return FunctionKey { - start_line: f.start_line as u32, - name: f.name as u32, - system_name: f.system_name as u32, - file_name: f.filename as u32, - }; - } -} - -impl PartialEq<Self> for FunctionKey { - fn eq(&self, other: &Self) -> bool { - return self.name == other.name - && self.system_name == other.system_name - && self.file_name == other.file_name - && self.start_line == other.start_line; - } -} - -impl Eq for FunctionKey {} - -impl Hash for FunctionKey { - fn hash<H: Hasher>(&self, state: &mut H) { - state.write_u32(self.name); - state.write_u32(self.system_name); - state.write_u32(self.file_name); - state.write_u32(self.start_line); - } -} - -struct MappingKey { - size: u64, - offset: u64, - build_id_or_file: i64, -} - -impl MappingKey { - fn get(m: &Mapping) -> MappingKey { - let mapsize_rounding = 0x1000; - let mut size = m.memory_limit - m.memory_start; - size = size + mapsize_rounding - 1; - size = size - (size % mapsize_rounding); - let mut k = MappingKey { - size: size, - offset: m.file_offset, - build_id_or_file: 0, - }; - if m.build_id != 0 { - k.build_id_or_file = m.build_id; - } - if m.filename != 0 { - k.build_id_or_file = m.filename; - } - k - } -} - -impl PartialEq<Self> for MappingKey { - fn eq(&self, other: &Self) -> bool { - return self.build_id_or_file == other.build_id_or_file - && self.offset == other.offset - && self.size == other.size; - } -} - -impl Eq for MappingKey {} - -impl Hash for MappingKey { - fn hash<H: Hasher>(&self, state: &mut H) { - state.write_i64(self.build_id_or_file); - state.write_u64(self.offset); - state.write_u64(self.size); - } -} - -struct LocationKey { - addr: u64, - lines: u64, - mapping_id: u64, -} - -impl LocationKey { - fn get(l: &Location) -> LocationKey { - return LocationKey { - addr: l.address, - lines: hash_lines(&l.line), - mapping_id: l.mapping_id, - }; - } -} - -impl PartialEq<Self> for LocationKey { - fn eq(&self, other: &Self) -> bool { - return self.lines == other.lines - && self.mapping_id == other.mapping_id - && self.addr == other.addr; - } -} - -impl Eq for LocationKey {} - -impl Hash for LocationKey { - fn hash<H: Hasher>(&self, state: &mut H) { - state.write_u64(self.lines); - state.write_u64(self.mapping_id); - state.write_u64(self.addr); - } -} - -fn hash_lines(s: &Vec<Line>) -> u64 { - let mut x = vec![0 as u64; s.len()]; - for i in 0..s.len() { - x[i] = s[i].function_id | ((s[i].line as u64) << 32) - } - let u64_arr = x.as_slice(); - let u8_arr: &[u8] = bytemuck::cast_slice(u64_arr); - return city_hash_64(u8_arr); -} - -struct SampleKey { - locations: u64, - labels: u64, -} - -impl SampleKey { - fn get(s: &Sample) -> SampleKey { - return SampleKey { - locations: hash_locations(&s.location_id), - labels: hash_labels(&s.label), - }; - } -} - -impl PartialEq<Self> for SampleKey { - fn eq(&self, other: &Self) -> bool { - return self.locations == other.locations && self.labels == other.labels; - } -} - -impl Eq for SampleKey {} - -impl Hash for SampleKey { - fn hash<H: Hasher>(&self, state: &mut H) { - state.write_u64(self.locations); - state.write_u64(self.labels); - } -} - -fn hash_labels(labels: &Vec<Label>) -> u64 { - if labels.len() == 0 { - return 0; - } - let mut _labels = labels.clone(); - _labels.sort_by(|a: &Label, b: &Label| -> Ordering { - if a.key < b.key || a.str < b.str { - return Ordering::Less; - } - Ordering::Greater - }); - let mut arr = vec![0 as u64; labels.len()]; - for i in 0..labels.len() { - arr[i] = (labels[i].key | labels[i].str << 32) as u64; - } - city_hash_64(bytemuck::cast_slice(&arr)) -} - -fn hash_locations(p0: &Vec<u64>) -> u64 { - let u8_arr: &[u8] = bytemuck::cast_slice(p0.as_slice()); - return city_hash_64(u8_arr); -} - -struct RewriteTable<K, V, M> { - k: fn(&V) -> K, - v: fn(&V) -> M, - t: HashMap<K, usize>, - s: Vec<M>, -} - -impl<K: std::cmp::Eq + std::hash::Hash, V, M> RewriteTable<K, V, M> { - fn new(size: usize, k: fn(&V) -> K, v: fn(&V) -> M) -> RewriteTable<K, V, M> { - RewriteTable { - k, - v, - t: HashMap::with_capacity(size), - s: Vec::new(), - } - } - - fn index(&mut self, dst: &mut Vec<u32>, values: &Vec<V>) { - for i in 0..values.len() { - let k = (self.k)(&values[i]); - let mut n = self.t.get(&k); - let _len = self.s.len().clone(); - if n.is_none() { - n = Some(&_len); - self.s.push((self.v)(&values[i])); - self.t.insert(k, *n.unwrap()); - } - dst[i] = *n.unwrap() as u32; - } - } - - /*fn append(&mut self, values: Vec<V>) { - for i in 0..values.len() { - let k = (self.k)(&values[i]); - let n = self.s.len(); - self.s.push((self.v)(&values[i])); - self.t.insert(k, n); - } - }*/ - fn values(&self) -> &Vec<M> { - return &self.s; - } -} - diff --git a/pyroscope/pprof-bin/src/utest.rs b/pyroscope/pprof-bin/src/utest.rs deleted file mode 100644 index 0efef076..00000000 --- a/pyroscope/pprof-bin/src/utest.rs +++ /dev/null @@ -1,1881 +0,0 @@ -#![allow(unused_assignments)] -use std::io::Read; -use crate::{merge_prof, CTX}; - -pub fn get_test_pprof_data() -> Vec<Vec<u8>> { - let pprofs_vec = TEST_PPROFS - .split(&"*******************") - .collect::<Vec<&str>>(); - let mut pprofs: Vec<Vec<u8>> = Vec::new(); - for pprof in pprofs_vec { - // Parsing HTTP request to get pprof data - let head_body = pprof.split(&"BODY:").collect::<Vec<&str>>(); - if head_body.len() < 2 { - continue; - } - let mut boundary = ""; - for hdr in head_body[0].split(&"\n").collect::<Vec<&str>>() { - if !hdr.starts_with("Content-Type: multipart/form-data; boundary=") { - continue; - } - boundary = &hdr[44..]; - } - let body = base64::decode(head_body[1].replace("\n", "").trim()).unwrap(); - let boundary_bytes = format!("--{}", boundary); - let parts = memchr::memmem::find_iter(&body, boundary_bytes.as_bytes()) - .fold((Vec::new(), 0), |(mut acc, last), pos| { - if pos > last { - acc.push(&body[last..pos]); - } - (acc, pos + boundary_bytes.len()) - }) - .0; - let pprof = &parts[0][118..]; - let mut decoder = flate2::read::GzDecoder::new(pprof); - let mut decompressed = Vec::new(); - decoder.read_to_end(&mut decompressed).unwrap(); - pprofs.push(decompressed); - } - pprofs -} - -#[cfg(test)] -mod tests { - use prost::Message; - use crate::{diff_tree, export_tree, merge_tree}; - use crate::pprof_pb::querier::v1::{FlameGraphDiff, SelectMergeStacktracesResponse}; - use super::*; - - - #[test] - fn test_select_merge_stacktraces() { - let pprofs = get_test_pprof_data(); - for pprof in pprofs { - - // Merging profiles into tree - merge_prof( - 0, - pprof.as_slice(), - "process_cpu:samples:count:cpu:nanoseconds".to_string(), - ); - } - //Asserting tree - let mut ctx = CTX.lock().unwrap(); - let tree = ctx.get_mut(&0).unwrap().lock().unwrap(); - let mut _names = tree.names.clone(); - _names.sort(); - assert_eq!(EXPECTED_NAMES, _names.join(", ")); - let mut map = "".to_string(); - let mut keys = tree.nodes.keys().cloned().collect::<Vec<u64>>(); - keys.sort(); - for i in keys { - let mut children_ids: Vec<String> = vec![]; - for n in &tree.nodes[&i] { - let str_node_id = n.node_id.to_string().clone(); - children_ids.push(str_node_id.clone()); - } - children_ids.sort(); - map = format!("{}{}: [{}]\n", map, i, children_ids.join(", ")); - } - assert_eq!(EXPECTED_MAP, map); - } - - #[test] - fn test_merge_trees() { - let req = "lQM4EJ5XpRIwoR5rOHMuaW8vYXBpL2F1dG9zY2FsaW5nL3YxLmluaXRFYiBVOCYNuEtnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci4oKmxvZ3NSZXF1ZXN0KS5FeHBvcnS7avKKFgtj2RlyZWZsZWN0Lm1hcGFzc2lnbl9mYXN0c3RyniLvClDdZNQ1Z2l0aHViLmNvbS9td2l0a293L2dvLWNvbm50cmFjay5kaWFsQ2xpZW50Q29ublRyYWNrZXKHvrqeyS/dNS1naXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLmRlY29kZVVpbnQ2NHMQR4RwgXBT0DVnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvLigqU2Vzc2lvbikudXBsb2FkRGF0YWOP1HtkwmPtGnJlZ2V4cC4oKlJlZ2V4cCkuZG9FeGVjdXRl1hmtUAzcCFNGZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL3Byb3RvLigqQmxvY2spLkVuY29kZUNvbHVtbgTvGlCF6nLBGHJlZ2V4cC5tYWtlT25lUGFzcy5mdW5jMYa6KjODtUD6KWdpdGh1Yi5jb20vc25vd2ZsYWtlZGIvZ29zbm93Zmxha2UuaW5pdC4zhxFhaZkCBnkuZ2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS4oKkxpbmUpLmVuY29kZTO+7trPy/thOGdpdGh1Yi5jb20vbXdpdGtvdy9nby1jb25udHJhY2suTmV3RGlhbENvbnRleHRGdW5jLmZ1bmMxOKhH8IQR9QBLZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci5idWlsZExhYmVsU2V0LmZ1bmMxymwXQT+p92dPZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3Ivc2VydmljZS90ZWxlbWV0cnkubmV3U2FtcGxlZExvZ2dlci5XcmFwQ29yZS5mdW5jMg2TSS6sZFJDG3J1bnRpbWUvZGVidWcuUmVhZEJ1aWxkSW5mbyXtvEZDLdz5P2dpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL21vZGVsL2xhYmVscy4oKkJ1aWxkZXIpLkxhYmVsc7LfK9W0QakgTmdpdGh1Yi5jb20vYXBhY2hlL2Fycm93L2dvL3YxNS9hcnJvdy9jb21wdXRlL2ludGVybmFsL2tlcm5lbHMuR2V0Q29tcGFyZUtlcm5lbEDCYcfITsOmC2ZtdC5GcHJpbnRmFjkDL11RpWUqZ2l0aHViLmNvbS9taXRjaGVsbGgvcmVmbGVjdHdhbGsud2Fsa1NsaWNlj+ftEr5A+3RYZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby9nb2RlbHRhcHJvZi9pbnRlcm5hbC9wcHJvZi4oKnByb2ZpbGVCdWlsZGVyKS5zdHJpbmdJbmRleGacV+uzc/yvQWdpdGh1Yi5jb20vYWxlY3Rob21hcy9wYXJ0aWNpcGxlL3YyLigqZ2VuZXJhdG9yQ29udGV4dCkucGFyc2VUZXJt7N8tqosTFuYicmVnZXhwL3N5bnRheC4oKnBhcnNlcikucGFyc2VDbGFzc93eY9mzZi7qLmdvLm9wZW50ZWxlbWV0cnkuaW8vcHJvdG8vb3RscC9tZXRyaWNzL3YxLmluaXSRY2O3J4YCaUBnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvcHJvdG8uKCpCbG9jaykuQXBwZW5kdB3UMdXNkcE2Z28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvY29uZm1hcC5OZXdGcm9tU3RyaW5nTWFwa41A1xmkGPBMZ2l0aHViLmNvbS9hbGVjdGhvbWFzL3BhcnRpY2lwbGUvdjIuKCpnZW5lcmF0b3JDb250ZXh0KS5wYXJzZVRlcm1Ob01vZGlmaWVyc6zmy1YLhh6CO2dpdGh1Yi5jb20vcHJvbWV0aGV1cy9jbGllbnRfZ29sYW5nL3Byb21ldGhldXMuTXVzdFJlZ2lzdGVyEQlPidk2qIAxZ2l0aHViLmNvbS9rbmFkaC9rb2FuZi9wcm92aWRlcnMvY29uZm1hcC5Qcm92aWRlcvSN/DlitF7QHnJlZ2V4cC9zeW50YXguKCpjb21waWxlcikuaW5zdBoJaBmHJBKqR2dpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vdXBzdHJlYW0vcmVtb3RlLigqUmVtb3RlKS51cGxvYWRQcm9maWxlExq2DGsKk+oRcnVudGltZS5zY2hlZGluaXTmWy3acgipcFFnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLmJhdGNoU2FtcGxlc0FuZFRpbWVTZXJpZXO1InxN+g8MBzJnby5vcGVudGVsZW1ldHJ5LmlvL290ZWwvYXR0cmlidXRlLmNvbXB1dGVEaXN0aW5jdB6lGk2m9bmZP2dpdGh1Yi5jb20vYXdzL2F3cy1zZGstZ28tdjIvc2VydmljZS9zMy9pbnRlcm5hbC9lbmRwb2ludHMuaW5pdIXQiFX0u+GCDWlvLkNvcHlCdWZmZXKfl+1+6jsc7lRnaXRodWIuY29tL3Byb21ldGhldXMvY2xpZW50X2dvbGFuZy9wcm9tZXRoZXVzLigqTWV0cmljVmVjKS5HZXRNZXRyaWNXaXRoTGFiZWxWYWx1ZXNVt8SFEJ/dQ1Fnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9wcm9jZXNzb3IvcHJvY2Vzc29yaGVscGVyLk5ld01ldHJpY3NQcm9jZXNzb3IuZnVuYzHU+/N/qpUCGGFnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLigqbWV0cmljc0V4cG9ydGVyKS5leHBvcnRTdW1tYXJ5RGF0YVBvaW504+I/ChkicOUMcnVudGltZS5tYWluJ0mDKZ1S0iJMZ29vZ2xlLmdvbGFuZy5vcmcvcHJvdG9idWYvcmVmbGVjdC9wcm90b3JlZ2lzdHJ5LigqRmlsZXMpLlJlZ2lzdGVyRmlsZS5mdW5jMmPoFr12xtVpN2dpdGh1Yi5jb20va2xhdXNwb3N0L2NvbXByZXNzL2ZsYXRlLigqY29tcHJlc3NvcikuY2xvc2VjdTi3XK1lzRhjb21wcmVzcy9mbGF0ZS5OZXdXcml0ZXJV52PsmAB49jlnaXRodWIuY29tL3Byb21ldGhldXMvY2xpZW50X2dvbGFuZy9wcm9tZXRoZXVzLk5ld1N1bW1hcnkXXBd0AnPTWhtydW50aW1lL3Bwcm9mLnByb2ZpbGVXcml0ZXI27TmsGdFln0pnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci5uZXdRdWV1ZVNlbmRlci5mdW5jMbbbw5u5qAo1PWdvb2dsZS5nb2xhbmcub3JnL3Byb3RvYnVmL2ludGVybmFsL2ZpbGVkZXNjLigqRmlsZSkubGF6eUluaXSpShfnknn0TRlyZWdleHAvc3ludGF4LmFwcGVuZFJhbmdluf2v+9l/AI8XY29tcHJlc3MvZ3ppcC5OZXdSZWFkZXI2MjsnJPFZb2NnaXRodWIuY29tL2FwYWNoZS9hcnJvdy9nby92MTUvYXJyb3cvY29tcHV0ZS9pbnRlcm5hbC9rZXJuZWxzLm51bWVyaWNDb21wYXJlS2VybmVsW2dvLnNoYXBlLnVpbnQxNl2IQ8a42Jma6UBnaXRodWIuY29tL3Byb21ldGhldXMvY2xpZW50X2dvbGFuZy9wcm9tZXRoZXVzLmlubGluZUxhYmVsVmFsdWVzOzevQGtZdk9EZ2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvZGlzY292ZXJ5L2ZpbGUubmV3RGlzY292ZXJlck1ldHJpY3MYdz9kf4AbFDdnaXRodWIuY29tL2FsZWN0aG9tYXMvcGFydGljaXBsZS92Mi4oKnN0cnVjdExleGVyKS5OZXh0r0rPjX20yxQuZ2l0aHViLmNvbS9zbm93Zmxha2VkYi9nb3Nub3dmbGFrZS5yZWFkQ0FDZXJ0c7U+on3o3hPceGdpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9yZWNlaXZlci9wcm9tZXRoZXVzcmVjZWl2ZXIvaW50ZXJuYWwuKCptZXRyaWNGYW1pbHkpLmFkZFNlcmllc3HrbMb7r59LMGdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28uKCpTZXNzaW9uKS5yZXNldIuKpmmPFI2HhQFnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcHJvY2Vzc29yL3Jlc291cmNlZGV0ZWN0aW9ucHJvY2Vzc29yL2ludGVybmFsLigqUmVzb3VyY2VQcm92aWRlcikuR2V0LmZ1bmMxUyTpN/63iZg3Z2l0aHViLmNvbS9rbGF1c3Bvc3QvY29tcHJlc3MvZmxhdGUuKCpmYXN0R2VuKS5hZGRCbG9ja2sbH0+B3i/9Kmdvb2dsZS5nb2xhbmcub3JnL3Byb3RvYnVmL3Byb3RvLlVubWFyc2hhbJhvjcbK/INxIXJlZ2V4cC9zeW50YXguKCpjb21waWxlcikuY29tcGlsZdpmYFT84JkSggFnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcHJvY2Vzc29yL3Jlc291cmNlZGV0ZWN0aW9ucHJvY2Vzc29yLigqcmVzb3VyY2VEZXRlY3Rpb25Qcm9jZXNzb3IpLlN0YXJ0yyTrCYCAOUc8Z2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby4oKmNwdVByb2ZpbGVDb2xsZWN0b3IpLnJlc2V0NrVRFAPRzEg1Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NoLWdvL3Byb3RvLkNvbFN0ci5FbmNvZGVDb2x1bW5uNZAWXBE3rEJnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqU3RyaW5nKS5FbmNvZGVpL4zajxSAZUtnb29nbGUuZ29sYW5nLm9yZy9wcm90b2J1Zi9pbnRlcm5hbC9pbXBsLigqTWVzc2FnZUluZm8pLm1ha2VLbm93bkZpZWxkc0Z1bmMAr4RMnC0//g9yZWdleHAubmV3UXVldWVNFP+P84T1QiRuZXQvaHR0cC4oKnRyYW5zZmVyV3JpdGVyKS53cml0ZUJvZHl4RA+910vDWDpnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmJhdGNoKS5jbG9zZVF1ZXJ5/D1r9HTT/l4jZ2l0aHViLmNvbS9rbmFkaC9rb2FuZi9tYXBzLkZsYXR0ZW59dNVVSV4znRxydW50aW1lL2RlYnVnLlBhcnNlQnVpbGRJbmZvahl4MFX1rx0uZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NoLWdvL2NvbXByZXNzLk5ld1dyaXRlcoG+WqHhYEsVKWdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3NlcnZpY2UuTmV3Vl+JUTm3ggcVYnl0ZXMuKCpCdWZmZXIpLldyaXRl8fWcs8fyn5gpazhzLmlvL2FwaW1hY2hpbmVyeS9wa2cvdXRpbC92ZXJzaW9uLmluaXTfvWLUu3glZhNzeW5jLigqT25jZSkuZG9TbG93U0BS/vivkVQPc3luYy4oKk9uY2UpLkRvuZpJIeCbCaFUZ2l0aHViLmNvbS9hcGFjaGUvYXJyb3cvZ28vdjE1L2Fycm93L2NvbXB1dGUvaW50ZXJuYWwva2VybmVscy5jb21wYXJlVGltZXN0YW1wS2VybmVs2+Vqf4iVU24vZ2l0aHViLmNvbS9hbGVjdGhvbWFzL3BhcnRpY2lwbGUvdjIubmV3VGFnTGV4ZXJ+EV/AEe3NPjxnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLigqUHJvZmlsZSkuV3JpdGVVbmNvbXByZXNzZWRnyQNPwDwFFCZnaXRodWIuY29tL2tuYWRoL2tvYW5mL3YyLigqS29hbmYpLlJhd01s9+Ub8u/DQ2dpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS4oKnRpbWVMaW1pdEFwcGVuZGVyKS5BcHBlbmRTk464pBKMT0JnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqU3RyaW5nKS5BcHBlbmR2QKlGCfVv1xlyZWdleHAvc3ludGF4LmFwcGVuZFRhYmxlaQV7ueCLd+U3Z2l0aHViLmNvbS9zaGlyb3UvZ29wc3V0aWwvdjMvaW50ZXJuYWwvY29tbW9uLlJlYWRMaW5lc3N/6Ll627GEK2dpdGh1Yi5jb20vZ29vZ2xlL3Bwcm9mL3Byb2ZpbGUuaW5pdC5mdW5jMzQCmBH3Vwz+EDNnaXRodWIuY29tL2dvb2dsZS9nby1jbXAvY21wL2ludGVybmFsL2Z1bmN0aW9uLmluaXR6r1KnWG8MDkVnb29nbGUuZ29sYW5nLm9yZy9wcm90b2J1Zi9pbnRlcm5hbC9maWxlZGVzYy4oKk1lc3NhZ2UpLnVubWFyc2hhbEZ1bGzcQV8CBaAKTyZnby5vcGVuY2Vuc3VzLmlvL3RyYWNlL3RyYWNlc3RhdGUuaW5pdNAxQmMhXlHaRGdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vdXBzdHJlYW0vcmVtb3RlLigqUmVtb3RlKS5zYWZlVXBsb2FkjaQWvWdujdIpZ2l0aHViLmNvbS9zcGYxMy9jb2JyYS4oKkNvbW1hbmQpLkV4ZWN1dGV2CZY3ryksKyJjb21wcmVzcy9nemlwLigqUmVhZGVyKS5yZWFkSGVhZGVypkNAh2MOyG4XbmV0LigqVENQQ29ubikuUmVhZEZyb20EHKJo0kKq5khnaXRodWIuY29tL2FsZWN0aG9tYXMvcGFydGljaXBsZS92Mi4oKmdlbmVyYXRvckNvbnRleHQpLnBhcnNlRGlzanVuY3Rpb26fHACX2u/Yszxnb29nbGUuZ29sYW5nLm9yZy9wcm90b2J1Zi9pbnRlcm5hbC9pbXBsLigqTWVzc2FnZUluZm8pLmluaXRyxe+cZRaZrC1naXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9mbGF0ZS5OZXdXcml0ZXLz2g+CteNedzhnaXRodWIuY29tL0NsaWNrSG91c2UvY2gtZ28vcHJvdG8uQ29sVUludDY0LkVuY29kZUNvbHVtbnmIgKkgBxhSUGdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL2Rpc2NvdmVyeS9maWxlLigqU0RDb25maWcpLk5ld0Rpc2NvdmVyZXJNZXRyaWNzMiqX5B7D9Oc0Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NoLWdvL3Byb3RvLigqQ29sSW50NjQpLkFwcGVuZNQ4biXbXBBZPGdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS4oKnNjcmFwZUxvb3ApLmFwcGVuZAHwXe6jqoKeJ21pbWUvbXVsdGlwYXJ0LigqV3JpdGVyKS5DcmVhdGVGb3JtRmlsZapX7tym+dC5PmdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3BkYXRhL3Bjb21tb24uTWFwLkVuc3VyZUNhcGFjaXR5lLcZPJ4gSzwpcmVnZXhwL3N5bnRheC4oKnBhcnNlcikucGFyc2VVbmljb2RlQ2xhc3MB63+sE8rWF3VnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvY2xpY2tob3VzZXByb2ZpbGVleHBvcnRlci9jaC4oKmNsaWNraG91c2VBY2Nlc3NOYXRpdmVDb2x1bW5hcikuSW5zZXJ0QmF0Y2g0jAaLg6QcPylnaXRodWIuY29tL3NpZ25hbGZ4L3NhcG0tcHJvdG8vZ2VuLmluaXQuMmaVWIHGfSWeEnJlZ2V4cC5tYWtlT25lUGFzc2/4CB5Ag1RCPmNvZGUuY2xvdWRmb3VuZHJ5Lm9yZy9nby1sb2dncmVnYXRvci9ycGMvbG9nZ3JlZ2F0b3JfdjIuaW5pdC4xz1NCYmhhw3B7Z2l0aHViLmNvbS9vcGVuLXRlbGVtZXRyeS9vcGVudGVsZW1ldHJ5LWNvbGxlY3Rvci1jb250cmliL3JlY2VpdmVyL3Byb21ldGhldXNyZWNlaXZlci9pbnRlcm5hbC4oKm1ldHJpY0ZhbWlseSkuYXBwZW5kTWV0cmljfmAFANfFpdJjZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci4oKm1ldHJpY3NFeHBvcnRlcikuZXhwb3J0SGlzdG9ncmFtRGF0YVBvaW50xU6LpkCFB7JIZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvY29uc3VtZXIuQ29uc3VtZU1ldHJpY3NGdW5jLkNvbnN1bWVNZXRyaWNzeVMzLnbfiC1CZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL2NvbHVtbi4oKlN0cmluZykuRGVjb2RlRYYGZEOumNYvZ2l0aHViLmNvbS92bXdhcmUvZ292bW9taS92aW0yNS90eXBlcy5pbml0LjY2NTiN2MfAyI0MImFnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLigqbWV0cmljc0V4cG9ydGVyKS5leHBvcnROdW1iZXJEYXRhUG9pbnRzWoDilPn0lqsjcmVnZXhwLigqUmVnZXhwKS5GaW5kQWxsU3RyaW5nSW5kZXhXCo4eZceNWDhnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9zY3JhcGUuTmV3TWFuYWdlci5mdW5jMe/73nFkCWTUIXJlZ2V4cC9zeW50YXguKCpwYXJzZXIpLm5ld1JlZ2V4cDgEhuweq+/YDnJlZ2V4cC5jb21waWxlZ6Pm42ry5KYPcnVudGltZS5kb0luaXQxiUxAWI6j/oIRcmVmbGVjdC5wYWNrRWZhY2V1VHScIpym/zVnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9wZGF0YS9wY29tbW9uLk1hcC5SYW5nZW3YQrh988ymNWdvLm9wZW50ZWxlbWV0cnkuaW8vb3RlbC9hdHRyaWJ1dGUuTmV3U2V0V2l0aEZpbHRlcmVk+vtcB7NctRBSZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby9nb2RlbHRhcHJvZi9pbnRlcm5hbC9wcHJvZi4oKnByb2ZpbGVCdWlsZGVyKS5CdWlsZOkKwwPkbClMP2dpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS4oKk1hbmFnZXIpLnJlbG9hZC5mdW5jMQGTAk22VXGeQWdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY2xpY2tob3VzZSkuUHJlcGFyZUJhdGNosp8jUAOOOnlEZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby91cHN0cmVhbS9yZW1vdGUuKCpSZW1vdGUpLmhhbmRsZUpvYnM3SvdiJIO6FSBnb2xhbmcub3JnL3gvbmV0L2h0bWwubWFwLmluaXQuMXvWVutQqS9uOGdpdGh1Yi5jb20vcHJvbWV0aGV1cy9jb21tb24vbW9kZWwubGFiZWxTZXRUb0ZpbmdlcnByaW505XfBbggt3eUzZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3Ivc2VydmljZS90ZWxlbWV0cnkuTmV3wDIn15sW0pcoZ2l0aHViLmNvbS9rbmFkaC9rb2FuZi92Mi4oKktvYW5mKS5NZXJnZdS0VTyplvttK2dpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLmRpYWyj5kTOgQKOhxhidWZpby4oKldyaXRlcikuUmVhZEZyb20EjeHw9nxiuiFuZXQvaHR0cC4oKlRyYW5zcG9ydCkuZGlhbENvbm5Gb3Ja6sJ3kzMCPEFnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqQXJyYXkpLmFwcGVuZNYU8VCyRjp5U2dpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vZ29kZWx0YXByb2YvaW50ZXJuYWwvcHByb2YuKCpwcm9maWxlQnVpbGRlcikuU2FtcGxlRID9h3CSBFcecmVnZXhwL3N5bnRheC4oKmNvbXBpbGVyKS5wbHVzECrEbECzld8pZ28ub3BlbnRlbGVtZXRyeS5pby9vdGVsL2F0dHJpYnV0ZS5OZXdTZXQQVv5mifCMhU9nby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9vdGVsY29sLigqQ29sbGVjdG9yKS5zZXR1cENvbmZpZ3VyYXRpb25Db21wb25lbnRz0iy5nzuYwE1AZ29vZ2xlLmdvbGFuZy5vcmcvcHJvdG9idWYvaW50ZXJuYWwvaW1wbC4oKk1lc3NhZ2VJbmZvKS5pbml0T25jZWzG8IPhmf1qRWdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS4oKnNjcmFwZUxvb3ApLnNjcmFwZUFuZFJlcG9ydI8Fh2IVyZG5OGdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL21vZGVsL2xhYmVscy5OZXdCdWlsZGVyP8LXQ7wWwSchZ2l0aHViLmNvbS9nb29nbGUvZ28tY21wL2NtcC5pbml0VNEGyfDZa+w8Z2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvZGlzY292ZXJ5LlJlZ2lzdGVyU0RNZXRyaWNz6IrtSrbQM15RZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvY29uZm1hcC9jb252ZXJ0ZXIvZXhwYW5kY29udmVydGVyLmNvbnZlcnRlci5Db252ZXJ0QLFP/uRlYTJJZ29vZ2xlLmdvbGFuZy5vcmcvcHJvdG9idWYvcmVmbGVjdC9wcm90b3JlZ2lzdHJ5LnJhbmdlVG9wTGV2ZWxEZXNjcmlwdG9yc2B9R0FRbnq4GHJlZ2V4cC4oKmJpdFN0YXRlKS5yZXNldBHY422UFFblN2dpdGh1Yi5jb20vYWxlY3Rob21hcy9wYXJ0aWNpcGxlL3YyLigqc3RydWN0TGV4ZXIpLlBlZWsTiaIjlg0qkWZnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvY2xpY2tob3VzZXByb2ZpbGVleHBvcnRlci4oKmNsaWNraG91c2VQcm9maWxlRXhwb3J0ZXIpLnNlbmSMTTGh1A0ejxJydW50aW1lLnByb2NyZXNpemViMQJUAbl+3R5uZXQvaHR0cC4oKlRyYW5zcG9ydCkuZGlhbENvbm5bG6mN1BROtjFnaXRodWIuY29tL21pdGNoZWxsaC9jb3B5c3RydWN0dXJlLigqd2Fsa2VyKS5FeGl0HQ8qYC7zV6BFZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci5idWlsZExhYmVsU2V0x7/pTomEYoopY29tcHJlc3MvZmxhdGUuKCpodWZmbWFuRW5jb2RlcikuZ2VuZXJhdGXeo7j8U3RTbRVyZWdleHAuY29tcGlsZU9uZVBhc3OblItOcCrHmjJnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLigqTG9jYXRpb24pLmVuY29kZYG//wcmlW+VOWdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS4oKnNjcmFwZUxvb3ApLnJ1bhJPBxpRI454OWdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY2xpY2tob3VzZSkuZGlhbCw5TOPg5tbHLmNvbXByZXNzL2ZsYXRlLigqaHVmZm1hbkJpdFdyaXRlcikuaW5kZXhUb2tlbnPU9pT1R4dAUCVnaXRodWIuY29tL21pdGNoZWxsaC9yZWZsZWN0d2Fsay5XYWxrtmOZvFfQUXcNaW8uY29weUJ1ZmZlcuslqltPGBehPGs4cy5pby9hcGltYWNoaW5lcnkvcGtnL3J1bnRpbWUuKCpTY2hlbWVCdWlsZGVyKS5BZGRUb1NjaGVtZeZtQquB7+RTEHN5bmMuKCpQb29sKS5QdXTZRZ40w4psXSNnby51YmVyLm9yZy96YXAvemFwY29yZS5uZXdDb3VudGVyc6osNZVHqmBLdGdpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9yZWNlaXZlci9wcm9tZXRoZXVzcmVjZWl2ZXIvaW50ZXJuYWwuKCp0cmFuc2FjdGlvbikuQXBwZW5kEXcFA9u5YtQxZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvY29uZm1hcC4oKkNvbmYpLkdldCJWzXtIJ3EbQmdvb2dsZS5nb2xhbmcub3JnL3Byb3RvYnVmL2ludGVybmFsL2ZpbGVkZXNjLigqRmlsZSkudW5tYXJzaGFsRnVsbGTValiyHiAxXGdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2V4cG9ydGVyL2V4cG9ydGVyaGVscGVyLigqbWV0cmljc1NlbmRlcldpdGhPYnNlcnZhYmlsaXR5KS5zZW5krTBQuxLAnYQpZ2l0aHViLmNvbS9zcGYxMy9jb2JyYS4oKkNvbW1hbmQpLmV4ZWN1dGV/1ujc51YLMSNnaXRodWIuY29tL2tuYWRoL2tvYW5mL21hcHMuZmxhdHRlbl2lQEZIiP3eKWVuY29kaW5nL2pzb24uKCplbmNvZGVTdGF0ZSkucmVmbGVjdFZhbHVlVq+T63lfgbUdZW5jb2RpbmcvYmluYXJ5LkFwcGVuZFV2YXJpbnTTReF7oONQHR1ydW50aW1lL3Bwcm9mLlN0YXJ0Q1BVUHJvZmlsZYzj/wC4cXnEDHJ1bnRpbWUubWFsZ8tbsYPCM9VdNmdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL290ZWxjb2wuKCpDb2xsZWN0b3IpLlJ1bk2rVs36YhM3FnJlZmxlY3QuKCpNYXBJdGVyKS5LZXm4wTFgPiVqXCdnaXRodWIuY29tL2tuYWRoL2tvYW5mL3YyLigqS29hbmYpLkxvYWTRLCUDoTBuiTpnaXRodWIuY29tL2FwYWNoZS9hcnJvdy9nby92MTUvYXJyb3cvaW50ZXJuYWwvZmxhdGJ1Zi5pbml00EIa/refsg8KaW8uUmVhZEFsbCVh4Gz5U7DUMmdpdGh1Yi5jb20vYWxlY3Rob21hcy9wYXJ0aWNpcGxlL3YyLnZhbGlkYXRlLmZ1bmMxHnazW7PsDQNPZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvZXhwb3J0ZXIvZXhwb3J0ZXJoZWxwZXIuKCpiYXNlUmVxdWVzdFNlbmRlcikuc2VuZB6DBGTYyJBZG3JlZ2V4cC4oKlJlZ2V4cCkuYWxsTWF0Y2hlcwcLXgCLgw2sLmdpdGh1Yi5jb20va2xhdXNwb3N0L2NvbXByZXNzL2ZsYXRlLm5ld0Zhc3RFbmPASL61bTKQii1naXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLmRlY29kZU1lc3NhZ2VlM4VigIrVz0ZnaXRodWIuY29tL2FwYWNoZS9hcnJvdy9nby92MTUvYXJyb3cvY29tcHV0ZS5SZWdpc3RlclNjYWxhckNvbXBhcmlzb25zRXbo4cPtvl8xZ2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS4oKlByb2ZpbGUpLmVuY29kZQQh3prQGK13OWdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3NlcnZpY2UvdGVsZW1ldHJ5Lm5ld0xvZ2dlcqN+FltfqZh5FGJ5dGVzLigqQnVmZmVyKS5ncm93Mh0go0kKtWMTcmVnZXhwL3N5bnRheC5QYXJzZcCvzk3kV6oRNGNvbXByZXNzL2ZsYXRlLigqaHVmZm1hbkJpdFdyaXRlcikud3JpdGVCbG9ja0R5bmFtaWNDbyG9rXsqojlnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9wZGF0YS9wY29tbW9uLmNvcHlCeXRlU2xpY2WUzwMNXMAaHUBnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvcHJvdG8uKCpCbG9jaykuRGVjb2RlXlokAy7HTTc4Z2l0aHViLmNvbS9pbmZsdXhkYXRhL2luZmx1eGRiLW9ic2VydmFiaWxpdHkvY29tbW9uLmluaXRmpU1TuNtGxyZrOHMuaW8vYXBpL25ldHdvcmtpbmcvdjEuYWRkS25vd25UeXBlc9BbN4J9cKZLQWdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpBcnJheSkuQXBwZW5klNZKXUWu6G0sZ2l0aHViLmNvbS9hd3MvYXdzLXNkay1nby9hd3MvZW5kcG9pbnRzLmluaXSSkZjMM1e5E11naXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvcmVjZWl2ZXIvcHlyb3Njb3BlcmVjZWl2ZXIvcHByb2ZwYXJzZXIuKCpwUHJvZlBhcnNlcikuUGFyc2X+Ftr9mf7M4BVyZWdleHAvc3ludGF4LkNvbXBpbGWCZYcqK9m87oYBZ2l0aHViLmNvbS9vcGVuLXRlbGVtZXRyeS9vcGVudGVsZW1ldHJ5LWNvbGxlY3Rvci1jb250cmliL3JlY2VpdmVyL3Byb21ldGhldXNyZWNlaXZlci9pbnRlcm5hbC4oKm1ldHJpY0ZhbWlseSkubG9hZE1ldHJpY0dyb3VwT3JDcmVhdGVi/88q373hER5jb21wcmVzcy9mbGF0ZS4oKldyaXRlcikuQ2xvc2UvrO2IBy979ipnaXRodWIuY29tL2tuYWRoL2tvYW5mL3YyLnBvcHVsYXRlS2V5UGFydHO7DbRiOa8JkjRnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmJhdGNoKS5TZW5kruNhD3nNJJEtZ2l0aHViLmNvbS9nb2xhbmcvcHJvdG9idWYvcHJvdG8uUmVnaXN0ZXJGaWxl4pqJRLZT3fBLZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvZXhwb3J0ZXIvZXhwb3J0ZXJoZWxwZXIuKCp0aW1lb3V0U2VuZGVyKS5zZW5kJFWdXKyFNSVgZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyL2NvbXByZXNzLigqRGVjb21wcmVzc29yKS5EZWNvbXByZXNzn0cPG3qBY2peZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby9nb2RlbHRhcHJvZi9pbnRlcm5hbC9wcHJvZi4oKkRlbHRhSGVhcFByb2ZpbGVyKS5Xcml0ZUhlYXBQcm90b4JGqG3paY9vHGNyeXB0by94NTA5LnBhcnNlQ2VydGlmaWNhdGVc2dePof8bjzxnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmNvbm5lY3QpLmZpcnN0QmxvY2upZyebvKX6P0VnaXRodWIuY29tL2FsZWN0aG9tYXMvcGFydGljaXBsZS92Mi4oKmdlbmVyYXRvckNvbnRleHQpLnN1YnBhcnNlR3JvdXCm6tgdTYsP1ltnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci4oKmxvZ3NFeHBvcnRlcldpdGhPYnNlcnZhYmlsaXR5KS5zZW5kJoC8rc4khuIgZ28udWJlci5vcmcvemFwLm9wdGlvbkZ1bmMuYXBwbHlu9DzXcMJ4n2JnaXRodWIuY29tL2FwYWNoZS9hcnJvdy9nby92MTUvYXJyb3cvY29tcHV0ZS9pbnRlcm5hbC9rZXJuZWxzLm51bWVyaWNDb21wYXJlS2VybmVsW2dvLnNoYXBlLmludDY0XeXifvX9/R+fRWdpdGh1Yi5jb20vYXBhY2hlL2Fycm93L2dvL3YxNS9hcnJvdy9jb21wdXRlLlJlZ2lzdGVyU2NhbGFyQXJpdGhtZXRpY3H+xbv30EVRV2dpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9jbGlja2hvdXNlcHJvZmlsZWV4cG9ydGVyL2NoLnJlYWRUcmVlRnJvbU1hcK1vawmntZNAF25ldC4oKlRDUENvbm4pLnJlYWRGcm9t/WB3GGhyxLAcY3J5cHRvL3g1MDkuUGFyc2VDZXJ0aWZpY2F0Zc19jzknADmSNGdpdGh1Yi5jb20vcHJvbWV0aGV1cy9jb21tb24vdmVyc2lvbi5jb21wdXRlUmV2aXNpb264KUrGkRqfRyVydW50aW1lL3Bwcm9mLigqcHJvZmlsZUJ1aWxkZXIpLmJ1aWxkVWxFi/Rw8917Z2l0aHViLmNvbS9vcGVuLXRlbGVtZXRyeS9vcGVudGVsZW1ldHJ5LWNvbGxlY3Rvci1jb250cmliL3JlY2VpdmVyL3Byb21ldGhldXNyZWNlaXZlci4oKnBSZWNlaXZlcikuaW5pdFByb21ldGhldXNDb21wb25lbnRzJ0yWTaQe3ZsfcnVudGltZS9wcHJvZi4oKnByb2ZNYXApLmxvb2t1cKR0B4JPvIQAQGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpUdXBsZSkucGFyc2UAOZHT70AWUjZnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9zZXJ2aWNlLigqU2VydmljZSkuU3RhcnRm8FvSdp60U0xnaXRodWIuY29tL2FwYWNoZS9hcnJvdy9nby92MTUvYXJyb3cvY29tcHV0ZS9pbnRlcm5hbC9rZXJuZWxzLkNvbXBhcmVLZXJuZWxzsZgWMRl01HBOZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvZXhwb3J0ZXIvZXhwb3J0ZXJoZWxwZXIuKCptZXRyaWNzUmVxdWVzdCkuRXhwb3J0GV7NEcF71aM8Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpiYXRjaCkuQXBwZW5kU3RydWN0yynLGQuHYlg2Z2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby4oKlNlc3Npb24pLlN0YXJ0LmZ1bmMyfiFs5fcLt1Q3Z28ub3BlbnRlbGVtZXRyeS5pby9vdGVsL2F0dHJpYnV0ZS5jb21wdXRlRGlzdGluY3RGaXhlZOcAlLIX44TUQGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpBcnJheSkucGFyc2WEGFZ37850AUVnaXRodWIuY29tL2FsZWN0aG9tYXMvcGFydGljaXBsZS92Mi4oKmdlbmVyYXRvckNvbnRleHQpLnBhcnNlTW9kaWZpZXL9YAu3/wRquThnaXRodWIuY29tL0NsaWNrSG91c2UvY2gtZ28vcHJvdG8uKCpDb2xTdHIpLkRlY29kZUNvbHVtbt7xqvk8uA0XKGdpdGh1Yi5jb20vbWl0Y2hlbGxoL3JlZmxlY3R3YWxrLndhbGtNYXAznk7U9cAkyTlnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmNvbm5lY3QpLnByb2Nlc3N8pINkIY+a8BFydW50aW1lLml0YWJzaW5pdNFhc/eUk3akM2dpdGh1Yi5jb20vYXBhY2hlL2Fycm93L2dvL3YxNS9hcnJvdy9jb21wdXRlLmluaXQuMGuYA2D5FQhvmwFnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9pbnRlcm5hbC9xdWV1ZS4oKmJvdW5kZWRNZW1vcnlRdWV1ZVtnby5zaGFwZS5pbnRlcmZhY2UgeyBFeHBvcnQoY29udGV4dC5Db250ZXh0KSBlcnJvcjsgSXRlbXNDb3VudCgpIGludCB9XSkuQ29uc3VtZT6NBZungtOkQ2dpdGh1Yi5jb20vcHJvbWV0aGV1cy9jbGllbnRfZ29sYW5nL3Byb21ldGhldXMuKCpzdW1tYXJ5KS5uZXdTdHJlYW2g4TitABqdLSVnaXRodWIuY29tL21pdGNoZWxsaC9yZWZsZWN0d2Fsay53YWxryd5YlGGGeJ9FZ2l0aHViLmNvbS9hbGVjdGhvbWFzL3BhcnRpY2lwbGUvdjIuKCpnZW5lcmF0b3JDb250ZXh0KS5wYXJzZVNlcXVlbmNl0/0Q0pm4xx0IbWFpbi5ydW598FZEdMGV2DZnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9vdGVsY29sLk5ld0NvbW1hbmQuZnVuYzGiQOiTrb/5iIoBZ2l0aHViLmNvbS9vcGVuLXRlbGVtZXRyeS9vcGVudGVsZW1ldHJ5LWNvbGxlY3Rvci1jb250cmliL3Byb2Nlc3Nvci9yZXNvdXJjZWRldGVjdGlvbnByb2Nlc3Nvci9pbnRlcm5hbC4oKlJlc291cmNlUHJvdmlkZXIpLmRldGVjdFJlc291cmNltsamCKjpwF4YcmVmbGVjdC4oKk1hcEl0ZXIpLlZhbHVl3FbTRJbqdzMqcnVudGltZS9wcHJvZi4oKnByb2ZpbGVCdWlsZGVyKS5hZGRDUFVEYXRhucbmosGJS8AacmVnZXhwLigqUmVnZXhwKS5iYWNrdHJhY2ueVBx0sUuwbyFuZXQvaHR0cC4oKnBlcnNpc3RDb25uKS53cml0ZUxvb3DO8zJW6A8a0xpnb2xhbmcub3JnL3gvbmV0L2h0bWwuaW5pdGVjHTvvJLw/XmdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9yZWNlaXZlci9weXJvc2NvcGVyZWNlaXZlci4oKnB5cm9zY29wZVJlY2VpdmVyKS5yZWFkUHJvZmlsZXMobQmR6ppTR15naXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvcmVjZWl2ZXIvcHlyb3Njb3BlcmVjZWl2ZXIuKCpweXJvc2NvcGVSZWNlaXZlcikuaGFuZGxlLmZ1bmMx4GmaFNc0t81CZ29vZ2xlLmdvbGFuZy5vcmcvcHJvdG9idWYvaW50ZXJuYWwvZmlsZWRlc2MuKCpFbnVtKS51bm1hcnNoYWxGdWxsyLhlNhCUjmo3azhzLmlvL2FwaW1hY2hpbmVyeS9wa2cvcnVudGltZS4oKlNjaGVtZSkuQWRkS25vd25UeXBlc4nlTjuTPGofJW5ldC9odHRwLigqdHJhbnNmZXJXcml0ZXIpLmRvQm9keUNvcHn0tn9Bk9hFYzBnaXRodWIuY29tL2hldHpuZXJjbG91ZC9oY2xvdWQtZ28vdjIvaGNsb3VkLmluaXTdSWWTl4uY2BpuZXQvaHR0cC4oKlRyYW5zcG9ydCkuZGlhbGzPhlbkJj3zE25ldC9odHRwLmdldENvcHlCdWYIouedHfF3iElnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci4oKnJldHJ5U2VuZGVyKS5zZW5krrScKuyF8rorZ2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS5pbml0LmZ1bmMzNlam1reBYa7lO2dpdGh1Yi5jb20va2xhdXNwb3N0L2NvbXByZXNzL2ZsYXRlLigqY29tcHJlc3Nvcikuc3RvcmVGYXN0JjBFPnJ/nIVSZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby9nb2RlbHRhcHJvZi9pbnRlcm5hbC9wcHJvZi4oKnByb2ZpbGVCdWlsZGVyKS5mbHVzaN0V+OGpeMe5LGdpdGh1Yi5jb20vZ29vZ2xlL3Bwcm9mL3Byb2ZpbGUuZW5jb2RlU3RyaW5nErlSuFmHsI5kZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci4oKm1ldHJpY3NFeHBvcnRlcikuZXhwb3J0SGlzdG9ncmFtRGF0YVBvaW50c5cMX2ZSavfAFWVuY29kaW5nL2pzb24uTWFyc2hhbDgYBl6c+QcsM2dvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2NvbmZtYXAuKCpDb25mKS5NZXJnZYuXUygpFoUFQmdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL290ZWxjb2wuKCpjb25maWdQcm92aWRlcikuR2V0Q29uZm1hcPGUZOnCfnNKSWdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpBcnJheSkuYXBwZW5kUm93UGxhaW4IwEsri+2Ih0NnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL2dvZGVsdGFwcm9mLigqSGVhcFByb2ZpbGVyKS5Qcm9maWxlQpvOrnEC5VIPcmVmbGVjdC5jb3B5VmFs2HLvg36RJEwvZ2l0aHViLmNvbS92bXdhcmUvZ292bW9taS92aW0yNS90eXBlcy5pbml0LjMzMjl+ZuUG2+nplUFnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqQXJyYXkpLkVuY29kZZkjNiPvBGXGWWdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vZ29kZWx0YXByb2YvaW50ZXJuYWwvcHByb2YuKCpwcm9maWxlQnVpbGRlcikuZW1pdExvY2F0aW9uFqZC3rsxuZk8Z2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby4oKmNwdVByb2ZpbGVDb2xsZWN0b3IpLlN0YXJ0Filh4TA595kpazhzLmlvL2NsaWVudC1nby9rdWJlcm5ldGVzL3NjaGVtZS5pbml0LjDHqVe+Mpvz7itydW50aW1lL3Bwcm9mLigqcHJvZmlsZUJ1aWxkZXIpLnN0cmluZ0luZGV4qaSJyrnT4i4vZ2l0aHViLmNvbS92bXdhcmUvZ292bW9taS92aW0yNS90eXBlcy5pbml0LjU1NDQVHkEdE3GPuitnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLmluaXQuZnVuYzMzS3NPCIqNAxUPYnl0ZXMuZ3Jvd1NsaWNlAEOjrwIwah8ZbmV0L2h0dHAuKCpSZXF1ZXN0KS53cml0ZSb3WRNj/VQOYGdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIuKCptZXRyaWNzRXhwb3J0ZXIpLmV4cG9ydE51bWJlckRhdGFQb2ludGmHQQapdt+WQWdvb2dsZS5nb2xhbmcub3JnL3Byb3RvYnVmL2ludGVybmFsL2ZpbGVkZXNjLigqRmlsZSkubGF6eUluaXRPbmNlFAk5soYt4vU3Z2l0aHViLmNvbS9rbGF1c3Bvc3QvY29tcHJlc3MvZmxhdGUuKCpmYXN0RW5jTDEpLkVuY29kZYbJTJnHlNe3J2dpdGh1Yi5jb20vbWl0Y2hlbGxoL2NvcHlzdHJ1Y3R1cmUuQ29wedqB5/qc11OoL2dpdGh1Yi5jb20vYWxlY3Rob21hcy9wYXJ0aWNpcGxlL3YyLnZpc2l0LmZ1bmMxhp1q68NVgbZMZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyLnBvc3RQcm9jZXNzUHJvZueviEiDFzI4OmdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS5tdXRhdGVTYW1wbGVMYWJlbHPVEUE8eRoiKUZnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9zZXJ2aWNlL3RlbGVtZXRyeS5uZXdTYW1wbGVkTG9nZ2VyLmZ1bmMxeMQkdQfiFaIuZ2l0aHViLmNvbS9taXRjaGVsbGgvY29weXN0cnVjdHVyZS5Db25maWcuQ29wed9HAeidKjSGFnJlZ2V4cC4oKlJlZ2V4cCkuU3BsaXTFHoEuYjXMJj5naXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmNvbm5lY3QpLnByZXBhcmVCYXRjaFhDnMz/bhOFFnJlZmxlY3QudmFsdWVJbnRlcmZhY2VLozmJfs8Ww2JnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLigqbWV0cmljc0V4cG9ydGVyKS5leHBvcnRTdW1tYXJ5RGF0YVBvaW50cxlAgBUdVdwjKmdpdGh1Yi5jb20vYmVvcm43L3BlcmtzL3F1YW50aWxlLm5ld1N0cmVhbQhPI5lUcIOeMWdpdGh1Yi5jb20vc2hpcm91L2dvcHN1dGlsL3YzL2NwdS5JbmZvV2l0aENvbnRleHTswx37vHT/lhNyZWdleHAvc3ludGF4LnBhcnNlRdw7yDNNF8l4Z2l0aHViLmNvbS9vcGVuLXRlbGVtZXRyeS9vcGVudGVsZW1ldHJ5LWNvbGxlY3Rvci1jb250cmliL3JlY2VpdmVyL3Byb21ldGhldXNyZWNlaXZlci9pbnRlcm5hbC4oKnRyYW5zYWN0aW9uKS5nZXRNZXRyaWNzQe+sEWcA+ksnZ2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS5tYXJzaGFsdiem6trMhpxLZ2l0aHViLmNvbS9hcGFjaGUvYXJyb3cvZ28vdjE1L2Fycm93L2NvbXB1dGUuKCpTY2FsYXJGdW5jdGlvbikuQWRkTmV3S2VybmVsghcE0fWUy28pZ2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS51bm1hcnNoYWycO4f5mQBcJRNidWZpby5OZXdSZWFkZXJTaXplY+uvmoIpeQ1GZ29vZ2xlLmdvbGFuZy5vcmcvcHJvdG9idWYvcmVmbGVjdC9wcm90b3JlZ2lzdHJ5LigqRmlsZXMpLlJlZ2lzdGVyRmlsZcKPWoQNhCwYRmdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL21vZGVsL2xhYmVscy4oKlNjcmF0Y2hCdWlsZGVyKS5MYWJlbHN8Kvsq9HmBXDZnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy91dGlsL3Bvb2wuKCpQb29sKS5HZXR8uSC3te6SNlxnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLigqbWV0cmljc0V4cG9ydGVyKS5jb2xsZWN0RnJvbU1ldHJpY6RcHK4AJlZTXmdpdGh1Yi5jb20vYXBhY2hlL2Fycm93L2dvL3YxNS9hcnJvdy9jb21wdXRlL2ludGVybmFsL2tlcm5lbHMuZ2VuQ29tcGFyZUtlcm5lbFtnby5zaGFwZS5pbnQ2NF3zFuU9AXpf2ClnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLlBhcnNlRGF0YaLxWJQ1BovgJGdpdGh1Yi5jb20vZ29vZ2xlL3Bwcm9mL3Byb2ZpbGUuaW5pdILzzCq5bhGqEHN5bmMuKCpQb29sKS5HZXTHnFsi+mCNvERnaXRodWIuY29tL2FsZWN0aG9tYXMvcGFydGljaXBsZS92Mi4oKmdlbmVyYXRvckNvbnRleHQpLnBhcnNlQ2FwdHVyZVqYaGu9a66xFG5ldC9odHRwLmluaXQuZnVuYzE1CNzc5O5P9k9AZ29vZ2xlLmdvbGFuZy5vcmcvcHJvdG9idWYvaW50ZXJuYWwvZmlsZWRlc2MuKCpGaWxlKS5sYXp5UmF3SW5pdEyvE1Gfodd5NmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jaC1nby9wcm90by4oKkNvbEZsb2F0NjQpLkFwcGVuZEkZAUeQHP6SMGdpdGh1Yi5jb20vZ29jY3kvZ28tanNvbi9pbnRlcm5hbC9kZWNvZGVyLmluaXQuMBfX57JdfULtTGdpdGh1Yi5jb20vcHJvbWV0aGV1cy9jbGllbnRfZ29sYW5nL3Byb21ldGhldXMuKCpTdW1tYXJ5VmVjKS5XaXRoTGFiZWxWYWx1ZXMYB0RLGoaC91dnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9pbnRlcm5hbC9mYW5vdXRjb25zdW1lci4oKm1ldHJpY3NDb25zdW1lcikuQ29uc3VtZU1ldHJpY3Nuj95sKWFd2itnaXRodWIuY29tL3Byb21ldGhldXMvY29tbW9uL3ZlcnNpb24uaW5pdC4wUcLOkyK4IFw3Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpzdHJ1Y3RNYXApLk1hcLBMxcEsnKvIKWdpdGh1Yi5jb20vYWxlY3Rob21hcy9wYXJ0aWNpcGxlL3YyLnZpc2l0d/i9I0NSe+k8Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjbGlja2hvdXNlKS5hY3F1aXJlSxLGj2HFhF8ac3luYy4oKnBvb2xDaGFpbikucHVzaEhlYWTFT4t+yiwqmCpnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLmluaXQuZnVuYzMgeNVumfTSQCZnaXRodWIuY29tL3NoaXJvdS9nb3BzdXRpbC92My9jcHUuSW5mb/gNyWb7NRhzOGdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28uKCpTZXNzaW9uKS50YWtlU25hcHNob3RzbCxxhR9+OhIjbWltZS9tdWx0aXBhcnQuKCpXcml0ZXIpLkNyZWF0ZVBhcnQZPIbLE+dNxERnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqQXJyYXkpLkFwcGVuZFJvdzRh4N8uzDyXIWNvbXByZXNzL2ZsYXRlLigqY29tcHJlc3NvcikuaW5pdDu2SyNbL4c2NmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqYmF0Y2gpLkFwcGVuZO4nijZDZWxiOWdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2NvbmZtYXAuKCpSZXNvbHZlcikuUmVzb2x2ZdPnXNpTJZF7DnN0cmluZ3MuU3BsaXROBtPtoIDTUXA3Z28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvY29tcG9uZW50LlN0YXJ0RnVuYy5TdGFydIXV91blegKkOmdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28uKCpTZXNzaW9uKS5kdW1wSGVhcFByb2ZpbGX5q8CtKXCuRBVydW50aW1lLm5ld3Byb2MuZnVuYzE9UvtV4pgpICJjb21wcmVzcy9mbGF0ZS4oKmNvbXByZXNzb3IpLmNsb3Nlaxdfzw2ZeT9aZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL2NsaWNraG91c2Vwcm9maWxlZXhwb3J0ZXIvY2guKCpMaW1pdGVkUG9vbCkucHV0r2F5b29ixi0ZcmVmbGVjdC5WYWx1ZS5TZXRNYXBJbmRleF0ka3od9QImH2VuY29kaW5nL2pzb24ubWFwRW5jb2Rlci5lbmNvZGV0SzZAZBi3yj1naXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9zY3JhcGUubmV3U2NyYXBlUG9vbC5mdW5jMS4xml99+XTH/1pVZ2l0aHViLmNvbS9wcm9tZXRoZXVzL2NsaWVudF9nb2xhbmcvcHJvbWV0aGV1cy4oKlN1bW1hcnlWZWMpLkdldE1ldHJpY1dpdGhMYWJlbFZhbHVlcyJRJZnkP+xZOmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLkRlZmF1bHREaWFsU3RyYXRlZ3nHw6zbn6AXnEVnaXRodWIuY29tL3Byb21ldGhldXMvY2xpZW50X2dvbGFuZy9wcm9tZXRoZXVzLnYyLk5ld1N1bW1hcnlWZWMuZnVuYzFrUeA/GEbNFBNydW50aW1lLnN5c3RlbXN0YWNrLE84EskYf7laZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci4oKm1ldHJpY3NFeHBvcnRlcikucHVzaE1ldHJpY3NEYXRhUL2517YSKW5dZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci4oKm1ldHJpY3NFeHBvcnRlcikuY29sbGVjdEZyb21NZXRyaWNzbsU7aCtzKlstZ29vZ2xlLmdvbGFuZy5vcmcvcHJvdG9idWYvcHJvdG8ucHJvdG9NZXRob2Rzr9tPzzOw+UwdY29tcHJlc3MvZmxhdGUubmV3RGVmbGF0ZUZhc3QLu7AJhmJD7ydnaXRodWIuY29tL3Nub3dmbGFrZWRiL2dvc25vd2ZsYWtlLmluaXTJggpX989njUNnaXRodWIuY29tL3Byb21ldGhldXMvY2xpZW50X2dvbGFuZy9wcm9tZXRoZXVzLigqUmVnaXN0cnkpLlJlZ2lzdGVyaOnvrquUdZhnZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyLm5ld1B5cm9zY29wZVJlY2VpdmVyLk5ld0RlY29tcHJlc3Nvci5mdW5jMgN8NNbAUF0+WWdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vZ29kZWx0YXByb2YvaW50ZXJuYWwvcHByb2YuKCpwcm9maWxlQnVpbGRlcikuTG9jc0ZvclN0YWNr36LtJrSkj9wOcnVudGltZS5ydDBfZ28COXYlrgR5gjpnb29nbGUuZ29sYW5nLm9yZy9wcm90b2J1Zi9pbnRlcm5hbC9maWxlZGVzYy5CdWlsZGVyLkJ1aWxk5gtCq4ZSTD8lZ2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS5QYXJzZfsUdDXHcwG9F2J5dGVzLigqQnVmZmVyKS5Xcml0ZVRvauJCgrio5IBGZ2l0aHViLmNvbS9hcGFjaGUvYXJyb3cvZ28vdjE1L2Fycm93L2NvbXB1dGUuR2V0RnVuY3Rpb25SZWdpc3RyeS5mdW5jMVVdBBt17qJnP2dpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY29ubmVjdCkucHJvZmlsZUV2ZW50cwNOxSAFwn4CPWdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uVHlwZS5Db2x1bW531UwrI2eH6UJnaXRodWIuY29tL2FsZWN0aG9tYXMvcGFydGljaXBsZS92Mi4oKmdlbmVyYXRvckNvbnRleHQpLnBhcnNlR3JvdXAg+kxktYb0Vk9naXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL2ludGVybmFsL3Bwcm9mLmRlZmF1bHRDb2xsZWN0b3IuU3RhcnRDUFVQcm9maWxlII0QYCKbGgM2Z2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby4oKlNlc3Npb24pLlN0YXJ0LmZ1bmMxc9TFdLUrT2o4Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjb25uZWN0KS5oYW5kbGU9+AdvzlPoji1naXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLmVuY29kZU1lc3NhZ2UEqS27C90LtiJnb2xhbmcub3JnL3gvbmV0L3RyYWNlLk5ld0V2ZW50TG9nzQIg18K+uHRoZ2l0aHViLmNvbS9vcGVuLXRlbGVtZXRyeS9vcGVudGVsZW1ldHJ5LWNvbGxlY3Rvci1jb250cmliL3JlY2VpdmVyL3Byb21ldGhldXNyZWNlaXZlci4oKnBSZWNlaXZlcikuU3RhcnS5O7Yh5ShtnBdyZWZsZWN0LlZhbHVlLkludGVyZmFjZX1JIRcgJHV4RGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpUdXBsZSkuQXBwZW5kUm93d2WVqxnPFMh/Z2l0aHViLmNvbS9vcGVuLXRlbGVtZXRyeS9vcGVudGVsZW1ldHJ5LWNvbGxlY3Rvci1jb250cmliL3JlY2VpdmVyL3Byb21ldGhldXNyZWNlaXZlci9pbnRlcm5hbC4oKm1ldHJpY0dyb3VwKS50b051bWJlckRhdGFQb2ludNU7+dU3ddmeHWNvbXByZXNzL2d6aXAuKCpSZWFkZXIpLlJlc2V0NP7THyFXhLBHZ2l0aHViLmNvbS9wcm9tZXRoZXVzL2NsaWVudF9nb2xhbmcvcHJvbWV0aGV1cy4oKlJlZ2lzdHJ5KS5NdXN0UmVnaXN0ZXJVt5j4PU22dBdydW50aW1lL3Bwcm9mLmFsbEZyYW1lc4CxNv8g2ZA+lgFnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9pbnRlcm5hbC9xdWV1ZS4oKkNvbnN1bWVyc1tnby5zaGFwZS5pbnRlcmZhY2UgeyBFeHBvcnQoY29udGV4dC5Db250ZXh0KSBlcnJvcjsgSXRlbXNDb3VudCgpIGludCB9XSkuU3RhcnQuZnVuYzHWgspixzv6NRNtYWluLnJ1bkludGVyYWN0aXZlqL0m8bupHKUlZ28udWJlci5vcmcvemFwLigqTG9nZ2VyKS5XaXRoT3B0aW9uc5ygUDMWbs2/QWdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpUdXBsZSkuRW5jb2RlKNhS2xnTBeIiY29tcHJlc3MvZmxhdGUuKCpkaWN0RGVjb2RlcikuaW5pdC9hGPaDA2CpDnJ1bnRpbWUuZG9Jbml0Jjpv6rz8iChAZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3Ivc2VydmljZS90ZWxlbWV0cnkubmV3U2FtcGxlZExvZ2dlcvhKxIFSiyKcLGdpdGh1Yi5jb20vZ29vZ2xlL3Bwcm9mL3Byb2ZpbGUuZW5jb2RlVmFyaW501qIVB2lwUVgqZ2l0aHViLmNvbS9zcGYxMy9jb2JyYS4oKkNvbW1hbmQpLkV4ZWN1dGVDBA1lTThBrFk8Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpiYXRjaENvbHVtbikuQXBwZW5kvZzntfonuktDZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvcGRhdGEvcG1ldHJpYy5NZXRyaWNTbGljZS5BcHBlbmRFbXB0efG3srkYVJApJGVuY29kaW5nL2pzb24uKCplbmNvZGVTdGF0ZSkubWFyc2hhbPZrq0pabMBNEHN0cmluZ3MuZ2VuU3BsaXR0sX4likIoYSVjb21wcmVzcy9mbGF0ZS4oKmNvbXByZXNzb3IpLmVuY1NwZWVkgZRAPuBk9UtPZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvcHJvY2Vzc29yL3Byb2Nlc3NvcmhlbHBlci4oKk9ic1JlcG9ydCkucmVjb3JkRGF0YW2M+lQEu8QpS2dpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS4oKnNjcmFwZUxvb3ApLnNjcmFwZUFuZFJlcG9ydC5mdW5jMVfTNiQ8eTh/O2dvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL290ZWxjb2wuKCpjb25maWdQcm92aWRlcikuR2V0Twewdx57jM9UZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvcHJvY2Vzc29yL3Byb2Nlc3NvcmhlbHBlci4oKk9ic1JlcG9ydCkuTWV0cmljc0FjY2VwdGVkpx/rZ7C7wDJGZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3Ivc2VydmljZS9pbnRlcm5hbC9ncmFwaC4oKkdyYXBoKS5TdGFydEFsbNuFLKdl9uhdM2dpdGh1Yi5jb20va2xhdXNwb3N0L2NvbXByZXNzL2ZsYXRlLigqV3JpdGVyKS5DbG9zZRr+3UaIAWtDB2lvLkNvcHmmiklCJ6fx+hpyZWdleHAubWVyZ2VSdW5lU2V0cy5mdW5jMqVBh/YnlIsyGnJlZmxlY3QubWFwYXNzaWduX2Zhc3RzdHIwLYm/+CpofN9LZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL2NvbHVtbi4oKkFycmF5KS5hcHBlbmRSb3dEZWZhdWx0Fs7UcVhdSp9cZ2l0aHViLmNvbS9wcm9tZXRoZXVzL2NsaWVudF9nb2xhbmcvcHJvbWV0aGV1cy4oKm1ldHJpY01hcCkuZ2V0T3JDcmVhdGVNZXRyaWNXaXRoTGFiZWxWYWx1ZXMaw9OpEfSu6UFnaXRodWIuY29tL2FsZWN0aG9tYXMvcGFydGljaXBsZS92Mi4oKmdlbmVyYXRvckNvbnRleHQpLnBhcnNlVHlwZc6m/PsaRIKLMmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jaC1nby9wcm90by4oKkNvbFN0cikuQXBwZW5ker30bSnZkmkScmVnZXhwLm9uZVBhc3NDb3B5dr07JCLDrKo6Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjb25uZWN0KS5zZW5kRGF0YfmCoykU1KSTPms4cy5pby9hcGltYWNoaW5lcnkvcGtnL3J1bnRpbWUuKCpTY2hlbWUpLkFkZEtub3duVHlwZVdpdGhOYW1ld7lT6tNBNVwQcnVudGltZS5uZXdwcm9jMYzjCXS65VefN2dpdGh1Yi5jb20vcHJvbWV0aGV1cy9jb21tb24vbW9kZWwuTGFiZWxTZXQuRmluZ2VycHJpbnSgbR/BHd3SAXRnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcmVjZWl2ZXIvcHJvbWV0aGV1c3JlY2VpdmVyL2ludGVybmFsLigqdHJhbnNhY3Rpb24pLkNvbW1pdPUsLVdwrqIEDnJlZ2V4cC5Db21waWxlQn8Fq9DEaoYwZ2l0aHViLmNvbS9nb2NjeS9nby1qc29uL2ludGVybmFsL2VuY29kZXIuaW5pdC4wXqwOKQFMmOdIZ29vZ2xlLmdvbGFuZy5vcmcvcHJvdG9idWYvaW50ZXJuYWwvaW1wbC4oKk1lc3NhZ2VJbmZvKS5tYWtlUmVmbGVjdEZ1bmNztzKp1VvqSjA3Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NoLWdvL3Byb3RvLkNvbEludDY0LkVuY29kZUNvbHVtbmxk++5fxgzjEnN0cmluZ3MuRmllbGRzRnVuY4gviolB4i2zOmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY29ubmVjdCkucmVhZERhdGGMD/BmkONiSDJydW50aW1lL3Bwcm9mLigqcHJvZmlsZUJ1aWxkZXIpLmFwcGVuZExvY3NGb3JTdGFja5b983FHOsXNOWdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2NvbmZtYXAuKCpSZXRyaWV2ZWQpLkFzQ29uZtgeAv+xLVozP2dpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY2xpY2tob3VzZSkuZGlhbC5mdW5jMXSTBL1j6QlmM2dvb2dsZS5nb2xhbmcub3JnL3Byb3RvYnVmL3JlZmxlY3QvcHJvdG9kZXNjLmluaXQuMKAFAAAAAAAAAACeVBx0sUuwb0cvpyMsVu4AAAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAF1wXdAJz01p7UfwdS9C/AAAAAAAAAAAAshcAAAAAAAAAAAAAAAAAAASN4fD2fGK6TB5zQt7DzgAAAAAAAAAAAFAAAAAAAAAAAAAAAAAAAADj4j8KGSJw5ekKOcErVZcAAAAAAAAAAABzsQAAAAAAAAAAAAAAAAAAgLE2/yDZkD41Ohl+pzj4AAAAAAAAAAAAb5EDAAAAAAAAAAAAAAAAAC9hGPaDA2CpAeAWqUK27gAAAAAAAAAAAF5EAQAAAAAAAAAAAAAAAAAgjRBgIpsaA+jn8GTGa44AAAAAAAAAAAA9AgAAAAAAAAAAAAAAAAAAgb//ByaVb5XMa11kSQqSAAAAAAAAAAAANYkBAAAAAAAAAAAAAAAAAAQcomjSQqrm4FoFT5ap8gAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAobQmR6ppTR27iu9hcprwAAAAAAAAAAAATzwAAAAAAAAAAAAAAAAAAyd5YlGGGeJ908MbPFzzTAAAAAAAAAAAAmgEAAAAAAAAAAAAAAAAAAOkKwwPkbClMujYmcCR1+AAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAACynyNQA446ecsbvBeEUPcAAAAAAAAAAACrKgAAAAAAAAAAAAAAAAAA2oHn+pzXU6gVdEoLAoGuAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAMecWyL6YI2875oXRMMJ0AAAAAAAAAAAAJoBAAAAAAAAAAAAAAAAAADfou0mtKSP3BwsJ1PKt9MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyynLGQuHYliX4J1kZtHKAAAAAAAAAAAADQAAAAAAAAAAAAAAAAAAAGtR4D8YRs0UH4we1XvnvQAAAAAAAAAAAJIEAAAAAAAA6OfwZMZrjgD4Dclm+zUYc7fSjjg7oBkBAAAAAAAAAAA9AgAAAAAAAMxrXWRJCpIAbMbwg+GZ/WpU+eja6nszAQAAAAAAAAAACokBAAAAAADMa11kSQqSABfX57JdfULtuaShoFT1DAEAAAAAAAAAACsAAAAAAAAA6Qo5wStVlwDT/RDSmbjHHQhRKAt3jmgBAAAAAAAAAABzsQAAAAAAABV0SgsCga4AsEzFwSycq8ihcYcbB7s+AQAAAAAAAAAAAEAAAAAAAABu4rvYXKa8AGVjHTvvJLw/RuoaPRYLHwEAAAAAAAAAABPPAAAAAAAAH4we1XvnvQD5q8CtKXCuRABmu7D5WgsBAAAAAAAAAACSBAAAAAAAAHtR/B1L0L8A3FbTRJbqdzO1AGVVz6pOAQAAAAAAAAAAQAAAAAAAAAB7UfwdS9C/ALgpSsaRGp9HKNxyzdWufQEAAAAAAAAAAHIXAAAAAAAAl+CdZGbRygAWpkLeuzG5me8uuclg5WEBAAAAAAAAAAANAAAAAAAAAEwec0Lew84AYjECVAG5ft0pp30PvZEsAQAAAAAAAAAAUAAAAAAAAADvmhdEwwnQABrD06kR9K7pmJ5UEkcAEQEAAAAAAAAAAJoBAAAAAAAAdPDGzxc80wBmnFfrs3P8rwoyoK8Bf2MBAAAAAAAAAACaAQAAAAAAABwsJ1PKt9MAExq2DGsKk+quLm7+7ZZEAQAAAAAAAAAAAAAAAAAAAABHL6cjLFbuAABDo68CMGofPGtVuliAeQEAAAAAAAAAAHAAAAAAAAAAAeAWqUK27gBno+bjavLkpre0KZyRDB0BAAAAAAAAAABeRAEAAAAAAOBaBU+WqfIAyd5YlGGGeJ85zCw+A5I7AQAAAAAAAAAAAEAAAAAAAADLG7wXhFD3ANAxQmMhXlHa3KncAIPqTQEAAAAAAAAAAKsqAAAAAAAANToZfqc4+ABrmANg+RUIbzM+2+qGyWgBAAAAAAAAAABvkQMAAAAAALo2JnAkdfgAF9fnsl19Qu19TEQJEdksAQAAAAAAAAAAAIAAAAAAAAAAZruw+VoLAXe5U+rTQTVcoTSWT5pq3AEAAAAAAAAAAJIEAAAAAAAAuaShoFT1DAGaX335dMf/WvR3FYtOAeMBAAAAAAAAAAArAAAAAAAAAJieVBJHABEBBByiaNJCquYMPprc7b/2AQAAAAAAAAAAmgEAAAAAAAC30o44O6AZAXHrbMb7r59LRmRTT9CL8QEAAAAAAAAAAD0CAAAAAAAAt7QpnJEMHQGs5stWC4YegsvM5dVxjIoBAAAAAAAAAABVFQAAAAAAALe0KZyRDB0BlNZKXUWu6G3L4pKxIML7ARyHAAAAAAAAHIcAAAAAAAC3tCmckQwdATSMBouDpBw/+xK/UIDVvwEAAAAAAAAAABAAAAAAAAAAt7QpnJEMHQHx9Zyzx/KfmAvwO6+ZRpMBAAAAAAAAAADpAgAAAAAAALe0KZyRDB0BP8LXQ7wWwSemFTXmMejwAQAAAAAAAAAAYgAAAAAAAAC3tCmckQwdAXSTBL1j6QlmD5Lz+UZajQEAAAAAAAAAAEkCAAAAAAAAt7QpnJEMHQHRYXP3lJN2pFUNDBFqr/IBAAAAAAAAAAA0UwAAAAAAALe0KZyRDB0BQn8Fq9DEaoarGZogImiSAQAAAAAAAAAAAAAAAAAAAAC3tCmckQwdAdhy74N+kSRMbjgiBMJ46QECAAAAAAAAAAIAAAAAAAAAt7QpnJEMHQFeWiQDLsdNN2BMvynsyqQBAAAAAAAAAACAAAAAAAAAALe0KZyRDB0BFilh4TA595kGevVGiB7iAQAAAAAAAAAABwAAAAAAAAC3tCmckQwdAQu7sAmGYkPv6O8ddRR5qAEAAAAAAAAAAFUAAAAAAAAAt7QpnJEMHQH0tn9Bk9hFY6FJpRcFnMUBAAAAAAAAAAALAAAAAAAAALe0KZyRDB0BOBCeV6USMKHgHlYCN4XFARwHAAAAAAAAHAcAAAAAAAC3tCmckQwdAUWGBmRDrpjWlRxBc0Yw+wEBAAAAAAAAAAEAAAAAAAAAt7QpnJEMHQECmBH3Vwz+EAyC6mLJg8QBAAAAAAAAAACYAAAAAAAAALe0KZyRDB0BqaSJyrnT4i6m4rOn3+PGAQMAAAAAAAAAAwAAAAAAAAC3tCmckQwdAW6P3mwpYV3aZnM8yoNj9wEAAAAAAAAAAAAgAAAAAAAAt7QpnJEMHQHd3mPZs2Yu6h0f8OQS+s0BYgAAAAAAAABiAAAAAAAAALe0KZyRDB0BSRkBR5Ac/pI36WPIx+TaAQAAAAAAAAAAAAAAAAAAAAC3tCmckQwdAR6lGk2m9bmZko33MfJ5rAEAAAAAAAAAAEkSAAAAAAAAt7QpnJEMHQHRLCUDoTBuibcukeL6iYYBAAAAAAAAAAAAAAAAAAAAALe0KZyRDB0BovFYlDUGi+AmeNyWkb2ZAQAAAAAAAAAASRIAAAAAAAC3tCmckQwdAW/4CB5Ag1RCV/wIxoJy7AEAAAAAAAAAAAMAAAAAAAAAt7QpnJEMHQHO8zJW6A8a0wHxu0aEjNkBAAAAAAAAAAAAAAAAAAAAALe0KZyRDB0BhroqM4O1QPqi9el/W4XrAQAAAAAAAAAAdgEAAAAAAAC3tCmckQwdAdxBXwIFoApP/C7L2MbWogEAAAAAAAAAAAcAAAAAAAAARuoaPRYLHwGGnWrrw1WBthPb/5wJtOoBqzoAAAAAAACrOgAAAAAAAEbqGj0WCx8BQ28hva17KqIpA447vWLOAUUAAAAAAAAARQAAAAAAAABG6ho9FgsfASRVnVyshTUlnD/Fo/KCzwEAAAAAAAAAABAAAAAAAAAARuoaPRYLHwGSkZjMM1e5E3stZSXvLb0BAAAAAAAAAADKjQAAAAAAAEbqGj0WCx8BVq+T63lfgbUeGz1+iJydAUkGAAAAAAAASQYAAAAAAAApp30PvZEsAd1JZZOXi5jY/2Fl+ZUX2AEAAAAAAAAAAFAAAAAAAAAAfUxECRHZLAGaX335dMf/WnC43rnFa7gBAAAAAAAAAAAAgAAAAAAAAFT56NrqezMB1DhuJdtcEFkVQbxUxyfUAQAAAAAAAAAAjwMBAAAAAABU+eja6nszAW2M+lQEu8QppNomV78KgAEAAAAAAAAAAFWFAAAAAAAAVPno2up7MwF8Kvsq9HmBXLdnFy+dfoEBAAAAAAAAAAAmAAAAAAAAADnMLD4DkjsBZpxX67Nz/K9yciyDzPqpAQAAAAAAAAAAAEAAAAAAAAChcYcbB7s+ASVh4Gz5U7DUHaiMd54DuwEAAAAAAAAAAABAAAAAAAAAri5u/u2WRAF8pINkIY+a8FwgLXg5M5EBAAAAAAAAAAAAAAAAAAAAAK4ubv7tlkQBjE0xodQNHo/NvzQ5KFD+AQAAAAAAAAAAAAAAAAAAAADcqdwAg+pNARoJaBmHJBKqXQvwWrEj9AEAAAAAAAAAAKsqAAAAAAAAtQBlVc+qTgEnTJZNpB7dmycjSF1Wb4sBQAAAAAAAAABAAAAAAAAAAO8uuclg5WEByyTrCYCAOUfj+ms4Qj38AQAAAAAAAAAADQAAAAAAAAAKMqCvAX9jAWuNQNcZpBjw4WLlNDvclQEAAAAAAAAAAJoBAAAAAAAACFEoC3eOaAHWgspixzv6NTXTuzmCWesBAAAAAAAAAABzsQAAAAAAADM+2+qGyWgBNu05rBnRZZ+C6DGyasH/AQAAAAAAAAAAb5EDAAAAAAA8a1W6WIB5AU0U/4/zhPVC6M6KHALv0AEAAAAAAAAAAHAAAAAAAAAAKNxyzdWufQFi/88q373hEUSPqOgA69wBAAAAAAAAAADkAAAAAAAAACjccs3Vrn0BY3U4t1ytZc1oWTSbTpX+AQgAAAAAAAAAFAAAAAAAAAAo3HLN1a59AYwP8GaQ42JIeKrtfV2F4QEAAAAAAAAAAHoWAAAAAAAApNomV78KgAGgbR/BHd3SAT1clooWVGMCAAAAAAAAAABVhQAAAAAAALdnFy+dfoEBVwqOHmXHjVgstd+IbuwFAiYAAAAAAAAAJgAAAAAAAADLzOXVcYyKATT+0x8hV4SwhvbzyQldQwIAAAAAAAAAAFUVAAAAAAAAD5Lz+UZajQFrGx9Pgd4v/eDxyK4WkVQCAAAAAAAAAABJAgAAAAAAAAvwO6+ZRpMB9SwtV3CuogT7IxpBQk0AAgAAAAAAAAAA6QIAAAAAAADhYuU0O9yVAcecWyL6YI28k40miaMVcwIAAAAAAAAAAJoBAAAAAAAAJnjclpG9mQH1LC1XcK6iBC0VHJ7f8kECAAAAAAAAAABJEgAAAAAAAPwuy9jG1qIB9SwtV3CuogRkZ8dT3V5XAgAAAAAAAAAABwAAAAAAAABgTL8p7MqkAfUsLVdwrqIEZuXRP+FmHQIAAAAAAAAAAIAAAAAAAAAA6O8ddRR5qAH1LC1XcK6iBKQdsRmcVTUCAAAAAAAAAABVAAAAAAAAAHJyLIPM+qkBa41A1xmkGPAxWNBPeXJSAgAAAAAAAAAAAEAAAAAAAACSjfcx8nmsAfUsLVdwrqIEKPbuQKAMIAIAAAAAAAAAAEkSAAAAAAAAcLjeucVruAGfl+1+6jsc7kkbBAbEr14CAAAAAAAAAAAAgAAAAAAAAB2ojHeeA7sB2oHn+pzXU6hDkxfZWsgVAgAAAAAAAAAAAEAAAAAAAAB7LWUl7y29AX4RX8AR7c0+6K/L3yZgYgIAAAAAAAAAAPIBAAAAAAAAey1lJe8tvQHmC0KrhlJMP+Hz61XAGC0CAAAAAAAAAADYiwAAAAAAAPsSv1CA1b8Buf2v+9l/AI9uX/HwYMV6AgAAAAAAAAAAEAAAAAAAAAAMgupiyYPEAfUsLVdwrqIEOfnbQDVIQQIAAAAAAAAAAJgAAAAAAAAAoUmlFwWcxQH1LC1XcK6iBB5xpDVmEGACAAAAAAAAAAALAAAAAAAAAJw/xaPygs8BaOnvrquUdZjHEZACh9sAAgAAAAAAAAAAEAAAAAAAAADozoocAu/QAYnlTjuTPGof3lM1ArxUOwIAAAAAAAAAAEAAAAAAAAAA6M6KHALv0AFsz4ZW5CY985+UtQhaTkgCAAAAAAAAAAAwAAAAAAAAABVBvFTHJ9QBdEs2QGQYt8rD14TXcv1eAgAAAAAAAAAAVlUAAAAAAAAVQbxUxyfUAU1s9+Ub8u/DkMiG9ehLZQIAAAAAAAAAADmOAAAAAAAAFUG8VMcn1AHCj1qEDYQsGPTfrM7F3gACACAAAAAAAAAAIAAAAAAAAP9hZfmVF9gBM77u2s/L+2HgNrqoXbZ8AgAAAAAAAAAAUAAAAAAAAAAB8btGhIzZATdK92Ikg7oV7GGPPXHVJgIAAAAAAAAAAAAAAAAAAAAAoTSWT5pq3AGM4/8AuHF5xOCMLcY5sRICkgQAAAAAAACSBAAAAAAAAESPqOgA69wBPVL7VeKYKSB8+kzxETcwAgAAAAAAAAAA5AAAAAAAAAB4qu19XYXhAVW3mPg9TbZ0ahpBSTmpJQJVFQAAAAAAAFUVAAAAAAAAeKrtfV2F4QHHqVe+Mpvz7rfRdkhy9GgCJQEAAAAAAAAlAQAAAAAAAAZ69UaIHuIB6yWqW08YF6FGrdyuejx3AgAAAAAAAAAABwAAAAAAAAD0dxWLTgHjAZ+X7X7qOxzuLABfp+RPNAIAAAAAAAAAACsAAAAAAAAANdO7OYJZ6wGNpBa9Z26N0vM/EC2lVhwCAAAAAAAAAABzsQAAAAAAAKL16X9bhesBr0rPjX20yxQOoEx3w6NeAgIAAAAAAAAAdgEAAAAAAABX/AjGgnLsAa7jYQ95zSSRGt59aUcMEQIAAAAAAAAAAAMAAAAAAAAAphU15jHo8AH1LC1XcK6iBIxk8GPLLiECAAAAAAAAAABiAAAAAAAAAEZkU0/Qi/EBEEeEcIFwU9COkGUXtLk/AgAAAAAAAAAAPQIAAAAAAABVDQwRaq/yAVNAUv74r5FUIG6RYDiLEwIAAAAAAAAAADRTAAAAAAAAXQvwWrEj9AEB8F3uo6qCnretll+MyF4CAAAAAAAAAACrKgAAAAAAAAw+mtztv/YByd5YlGGGeJ+w69uJeB4lAgAAAAAAAAAAmgEAAAAAAABmczzKg2P3Ac19jzknADmSGpQD2erWZAIAAAAAAAAAAAAgAAAAAAAA4/prOEI9/AEg+kxktYb0VnUUbpmwyRkCAAAAAAAAAAANAAAAAAAAAGhZNJtOlf4BNGHg3y7MPJf8/85ffbNiAggAAAAAAAAACAAAAAAAAABoWTSbTpX+Aa/bT88zsPlM7b42795tDQIEAAAAAAAAAAQAAAAAAAAAgugxsmrB/wEedrNbs+wNAxGB9CgPjgECAAAAAAAAAABvkQMAAAAAAPsjGkFCTQACOASG7B6r79iinVaF/EX3AgAAAAAAAAAA6QIAAAAAAADHEZACh9sAArn9r/vZfwCP++oRwHmBvQIAAAAAAAAAABAAAAAAAAAAEYH0KA+OAQJk1WpYsh4gMZAZHjHtUtQCAAAAAAAAAACJDgIAAAAAABGB9CgPjgECpurYHU2LD9bYaIc2XRTaAgAAAAAAAAAA5oIBAAAAAAAa3n1pRwwRAgI5diWuBHmCON7sTSqxkgIAAAAAAAAAAAMAAAAAAAAAIG6RYDiLEwLfvWLUu3glZn48ube13ZMCAAAAAAAAAAA0UwAAAAAAAEOTF9layBUCsEzFwSycq8hYI3mB3Gi7AgAAAAAAAAAAAEAAAAAAAAB1FG6ZsMkZAtNF4Xug41Ado2IqQHeL1gINAAAAAAAAAA0AAAAAAAAA8z8QLaVWHALWohUHaXBRWJm1KxxIjKQCAAAAAAAAAABzsQAAAAAAAGbl0T/hZh0COASG7B6r79hX3QFHK2+LAgAAAAAAAAAAgAAAAAAAAAAo9u5AoAwgAjIdIKNJCrVjvkXXRIbLpwIAAAAAAAAAAEkSAAAAAAAAjGTwY8suIQI4BIbsHqvv2AMEpT5vMvMCAAAAAAAAAABiAAAAAAAAALDr24l4HiUCZpxX67Nz/K+HhAUkAwLPAgAAAAAAAAAAmgEAAAAAAADh8+tVwBgtAvMW5T0Bel/YCwGeKrcBoQIAAAAAAAAAAKuKAAAAAAAA4fPrVcAYLQLQQhr+t5+yDwyQNHPBrtICLQEAAAAAAAAtAQAAAAAAAHz6TPERNzACdLF+JYpCKGEqNW2SGjzHAgAAAAAAAAAA5AAAAAAAAAAsAF+n5E80AhbO1HFYXUqfToG6N/iGmAIAAAAAAAAAACsAAAAAAAAApB2xGZxVNQI4BIbsHqvv2Cl2oEjiNfsCAAAAAAAAAABVAAAAAAAAAN5TNQK8VDsChdCIVfS74YLzaFEafJaYAgAAAAAAAAAAQAAAAAAAAACOkGUXtLk/AoXV91blegKkoiH/M4y4hQIAAAAAAAAAAD0CAAAAAAAAOfnbQDVIQQIyHSCjSQq1Y4+0pVbDwLICAAAAAAAAAACYAAAAAAAAAC0VHJ7f8kECMh0go0kKtWOMycSRmlW0AgAAAAAAAAAASRIAAAAAAACG9vPJCV1DAsmCClf3z2eNfcJPQs9WgQJVFQAAAAAAAFUVAAAAAAAAn5S1CFpOSAKC88wquW4Rql+Mke0KOJgCAAAAAAAAAAAwAAAAAAAAADFY0E95clICx5xbIvpgjbzCtXI4BLLjAgAAAAAAAAAAAEAAAAAAAADg8ciuFpFUAm7FO2grcypbrd53g372jAIAAAAAAAAAAEkCAAAAAAAAZGfHU91eVwI4BIbsHqvv2NM1u7O3CcgCAAAAAAAAAAAHAAAAAAAAAA6gTHfDo14C/WB3GGhyxLBRH1y/6m/uAgAAAAAAAAAAdAEAAAAAAABJGwQGxK9eAohDxrjYmZrpZW9ivr1RqwIAgAAAAAAAAACAAAAAAAAAt62WX4zIXgIa/t1GiAFrQ3qunFUXs7YCAAAAAAAAAACrCgAAAAAAALetll+MyF4CbCxxhR9+OhJh7pRUcsWvAgAAAAAAAAAAACAAAAAAAADD14TXcv1eAueviEiDFzI4bl+CojHhqQIAAAAAAAAAAFUVAAAAAAAAw9eE13L9XgKPBYdiFcmRuV86mEaZ0L4CAUAAAAAAAAABQAAAAAAAAB5xpDVmEGACOASG7B6r79haymaHBqaUAgAAAAAAAAAACwAAAAAAAADor8vfJmBiAlZfiVE5t4IHYuh6Bchf9gIAAAAAAAAAABMAAAAAAAAA6K/L3yZgYgJB76wRZwD6SxolkI7fLZ4CAAAAAAAAAADfAQAAAAAAAD1clooWVGMCRdw7yDNNF8kHkI022nbEAgAAAAAAAAAAVWUAAAAAAAA9XJaKFlRjAhgHREsahoL3bC76ZIAzsAIAAAAAAAAAAAAgAAAAAAAAGpQD2erWZAINk0kurGRSQ3sZ+vUdVsECAAAAAAAAAAAAIAAAAAAAAJDIhvXoS2UCqiw1lUeqYEtUfJlhmY2qAgAAAAAAAAAAOY4AAAAAAACTjSaJoxVzAhrD06kR9K7pBhY9jsqNrAIAAAAAAAAAAJoBAAAAAAAARq3crno8dwLrJapbTxgXoT+Ub9MsqPMCAAAAAAAAAAAHAAAAAAAAAG5f8fBgxXoC1Tv51Td12Z5+zDbFEbiEAgAAAAAAAAAAEAAAAAAAAADgNrqoXbZ8Ap4i7wpQ3WTUQNxC6KV3ngIAAAAAAAAAAFAAAAAAAAAAfsw2xRG4hAJ2CZY3ryksKy5+Q3jfexQDAAAAAAAAAAAQAAAAAAAAAKIh/zOMuIUCCMBLK4vtiIeVvbhnSNYYAwsAAAAAAAAAPQIAAAAAAABX3QFHK2+LAt6juPxTdFNtc91NcYejFAMAAAAAAAAAAIAAAAAAAAAArd53g372jAKfHACX2u/Ys6tjlwGqiyUDAAAAAAAAAABJAgAAAAAAADje7E0qsZICY+uvmoIpeQ2rHV9scxlZAwAAAAAAAAAAAwAAAAAAAAB+PLm3td2TAmriQoK4qOSAdRg5/SyrRAMAAAAAAAAAADRTAAAAAAAAWspmhwamlALeo7j8U3RTbUICUBlJ5B4DAAAAAAAAAAALAAAAAAAAAF+Mke0KOJgCWphoa71rrrFj/vIrCwtjAzAAAAAAAAAAMAAAAAAAAABOgbo3+IaYAsfDrNufoBec2/4omNFRHQMAAAAAAAAAACsAAAAAAAAA82hRGnyWmAK2Y5m8V9BRdwSOQhuerl4DAAAAAAAAAABAAAAAAAAAABolkI7fLZ4CRXbo4cPtvl8PhQTHNTgiAwAAAAAAAAAA3wEAAAAAAABA3ELopXeeAgSpLbsL3Qu2IKgTEYM5OwNQAAAAAAAAAFAAAAAAAAAACwGeKrcBoQKCFwTR9ZTLb1Z2lcQC1U8DAAAAAAAAAACrigAAAAAAAJm1KxxIjKQCrTBQuxLAnYTtEIRKqeVhAwAAAAAAAAAAc7EAAAAAAAC+RddEhsunAu/73nFkCWTUW3WpTnQxMgNJEgAAAAAAAEkSAAAAAAAAbl+CojHhqQIl7bxGQy3c+ehAFJ7vGyADVRUAAAAAAABVFQAAAAAAAFR8mWGZjaoCtT6ifejeE9x/AK4qOJxwAwCAAAAAAAAAAIAAAAAAAABUfJlhmY2qAoJlhyor2bzupscRXObUcwM5DgAAAAAAADkOAAAAAAAABhY9jsqNrAIEHKJo0kKq5qs0EJgRIDQDAAAAAAAAAACaAQAAAAAAAGHulFRyxa8CQMJhx8hOw6Y+6owDMX8lAwAAAAAAAAAAACAAAAAAAABsLvpkgDOwAsVOi6ZAhQeyHW2ne0nAHAMAAAAAAAAAAAAgAAAAAAAAj7SlVsPAsgLswx37vHT/lmu8x14FsSMDAAAAAAAAAACYAAAAAAAAAIzJxJGaVbQC7MMd+7x0/5bu3PQ3AmVUAwAAAAAAAAAASRIAAAAAAAB6rpxVF7O2ArZjmbxX0FF3r5P20+RTEAMAAAAAAAAAAKsKAAAAAAAAWCN5gdxouwIlYeBs+VOw1Fx75msm/gYDAAAAAAAAAAAAQAAAAAAAAPvqEcB5gb0C1Tv51Td12Z6iu3vsV0UhAwAAAAAAAAAAEAAAAAAAAAB7Gfr1HVbBAn101VVJXjOdMeVN35jkAgMAIAAAAAAAAAAgAAAAAAAAB5CNNtp2xAK9nOe1+ie6S1vz3LZBYEkDACAAAAAAAAAAIAAAAAAAAAeQjTbadsQCz1NCYmhhw3Dc04joD4w2AwAAAAAAAAAAVUUAAAAAAAAqNW2SGjzHAsCvzk3kV6oRz6MshJfVaQMAAAAAAAAAAOQAAAAAAAAA0zW7s7cJyALeo7j8U3RTbbPgE8UDSF4DAAAAAAAAAAAHAAAAAAAAAIeEBSQDAs8Ca41A1xmkGPBR+10jf981AwAAAAAAAAAAmgEAAAAAAACQGR4x7VLUAgii550d8XeIPEYDjILIEAMAAAAAAAAAAIkOAgAAAAAA2GiHNl0U2gIIouedHfF3iE6fl+xodVwDAAAAAAAAAADmggEAAAAAAMK1cjgEsuMCGsPTqRH0rumMEhiFws0IAwAAAAAAAAAAAEAAAAAAAABRH1y/6m/uAoJGqG3paY9v/gG/3pbIHwN0AQAAAAAAAHQBAAAAAAAAAwSlPm8y8wLeo7j8U3RTbbqKtRx/nBwDAAAAAAAAAABiAAAAAAAAAD+Ub9MsqPMCZqVNU7jbRse/mitScvdyAwAAAAAAAAAABwAAAAAAAABi6HoFyF/2AqN+FltfqZh5bQ8YCbLJWQMAAAAAAAAAABMAAAAAAAAAop1WhfxF9wL+Ftr9mf7M4B+VRYBrFWIDAAAAAAAAAADpAgAAAAAAACl2oEjiNfsC/hba/Zn+zODFzUv49ygbAwAAAAAAAAAAVQAAAAAAAABce+ZrJv4GA9qB5/qc11Oo2YdJmklcxQMAAAAAAAAAAABAAAAAAAAAjBIYhcLNCAMEHKJo0kKq5vM6hiiRQ/cDAAAAAAAAAAAAQAAAAAAAAK+T9tPkUxAD+xR0NcdzAb0irUXQOqqeAwAAAAAAAAAAqwoAAAAAAAA8RgOMgsgQA+KaiUS2U93wvKhqYQGC3wMAAAAAAAAAAIkOAgAAAAAALn5DeN97FAMo2FLbGdMF4v2z718WrPADEAAAAAAAAAAQAAAAAAAAAHPdTXGHoxQDAK+ETJwtP/568/PKryfFA4AAAAAAAAAAgAAAAAAAAACVvbhnSNYYA59HDxt6gWNqvL/XNRcdngMAAAAAAAAAADICAAAAAAAAxc1L+PcoGwOYb43GyvyDcfLRsdH8rI0DAAAAAAAAAABVAAAAAAAAALqKtRx/nBwDZpVYgcZ9JZ6lhw0qrsmWAwAAAAAAAAAAYgAAAAAAAAAdbad7ScAcA8VOi6ZAhQey8zST/zoKgQMAAAAAAAAAAAAgAAAAAAAA2/4omNFRHQM+jQWbp4LTpImlFQYFip8DAAAAAAAAAAArAAAAAAAAAEICUBlJ5B4Der30bSnZkmkfVysd4UjJAwsAAAAAAAAACwAAAAAAAACiu3vsV0UhA3YJljevKSwrcdxYT2u9pAMAAAAAAAAAABAAAAAAAAAAD4UExzU4IgM9+AdvzlPojnAS+/1f7P4DAAAAAAAAAACrAAAAAAAAAA+FBMc1OCID+ErEgVKLIpwt44CrsGLjAxIAAAAAAAAAEgAAAAAAAAAPhQTHNTgiA90V+OGpeMe5NBQIXoT3igMiAQAAAAAAACIBAAAAAAAAa7zHXgWxIwPs3y2qixMW5r9uJueqG6kDAAAAAAAAAACYAAAAAAAAAD7qjAMxfyUDVl+JUTm3ggcuOX8T0KHWAwAAAAAAAAAAACAAAAAAAACrY5cBqoslA9IsuZ87mMBNPsMPYGh3hQMAAAAAAAAAAEkCAAAAAAAAqzQQmBEgNAPJ3liUYYZ4n/vEWS49z9QDAAAAAAAAAACaAQAAAAAAAFH7XSN/3zUDx5xbIvpgjbwDeA/oaIisAwAAAAAAAAAAmgEAAAAAAADc04joD4w2A3dllasZzxTItmj1zhhhnQMAAAAAAAAAAFVFAAAAAAAAdRg5/SyrRANlM4VigIrVz/PQtV22faMDAAAAAAAAAACaOQAAAAAAAHUYOf0sq0QD5eJ+9f39H58Kfh9b9pWWAwAAAAAAAAAAmhkAAAAAAABWdpXEAtVPA8BIvrVtMpCKa4XZPtq7+wMAAAAAAAAAAKuKAAAAAAAA7tz0NwJlVAPv+95xZAlk1Kk4PYOMMrcDSRIAAAAAAABJEgAAAAAAAKsdX2xzGVkDQLFP/uRlYTLGT3tCkv7sAwAAAAAAAAAAAwAAAAAAAABtDxgJsslZA0tzTwiKjQMVzYFzgGZyqwMTAAAAAAAAABMAAAAAAAAATp+X7Gh1XAPimolEtlPd8DerECiaa90DAAAAAAAAAADmggEAAAAAALPgE8UDSF4Der30bSnZkmkAa7OysT3FAwcAAAAAAAAABwAAAAAAAAAEjkIbnq5eA6PmRM6BAo6HLII3HUYFmQMAAAAAAAAAAEAAAAAAAAAA7RCESqnlYQN98FZEdMGV2OuTMOEKwukDAAAAAAAAAABzsQAAAAAAAB+VRYBrFWIDmG+Nxsr8g3GtsnaWVju/AwAAAAAAAAAA6QIAAAAAAADPoyyEl9VpAyw5TOPg5tbHo1mGVYYBhwMAAAAAAAAAAOQAAAAAAAAAv5orUnL3cgPIuGU2EJSOaj4ALFFtrsQDAAAAAAAAAAAHAAAAAAAAAPM0k/86CoEDVbfEhRCf3UPv5BZSpbRkBAAAAAAAAAAAACAAAAAAAAA+ww9gaHeFA16sDikBTJjnN93SNL5dIwQAAAAAAAAAAEkCAAAAAAAAo1mGVYYBhwPHv+lOiYRiilqJkz5nYiUE5AAAAAAAAADkAAAAAAAAAPLRsdH8rI0DmG+Nxsr8g3GHFtxlD2QPBAAAAAAAAAAAVQAAAAAAAAAKfh9b9pWWA3YnpurazIacT+1qExp7bQSaGQAAAAAAAJoZAAAAAAAApYcNKq7JlgME7xpQhepywc0HO66cVlQEAAAAAAAAAABiAAAAAAAAACyCNx1GBZkDGv7dRogBa0MlYRJ0PYM3BAAAAAAAAAAAQAAAAAAAAAC2aPXOGGGdA6pX7tym+dC5WDV8EBguFQRVRQAAAAAAAFVFAAAAAAAAvL/XNRcdngMDfDTWwFBdPpst3wEgexwEAAAAAAAAAAAmAgAAAAAAALy/1zUXHZ4D1hTxULJGOnkNPIgaEzB8BAAAAAAAAAAAAgAAAAAAAAC8v9c1Fx2eA/r7XAezXLUQ8Hu5Z+PaaAQAAAAAAAAAAAoAAAAAAAAAIq1F0DqqngNWX4lRObeCB+Fbb9UUeVgEAAAAAAAAAACrCgAAAAAAAImlFQYFip8DGUCAFR1V3CMpZgsiWdRaBCsAAAAAAAAAKwAAAAAAAADz0LVdtn2jA2bwW9J2nrRTs45K1hwzIQQAAAAAAAAAAJo5AAAAAAAAcdxYT2u9pAMo2FLbGdMF4trpcDLFcj8EEAAAAAAAAAAQAAAAAAAAAL9uJueqG6kDlLcZPJ4gSzwVDdtSlwo1BAAAAAAAAAAAmAAAAAAAAAADeA/oaIisAxrD06kR9K7parTwRTh2MQQAAAAAAAAAAJoBAAAAAAAArbJ2llY7vwOYb43GyvyDcQxqR0kWOEgEAAAAAAAAAADpAgAAAAAAAD4ALFFtrsQD+YKjKRTUpJNo5uQYYM9eBAcAAAAAAAAABwAAAAAAAADZh0maSVzFA7BMxcEsnKvIAJoLEVFsBwQAAAAAAAAAAABAAAAAAAAA+8RZLj3P1ANmnFfrs3P8r7mVgh8P+20EAAAAAAAAAACaAQAAAAAAAC45fxPQodYDo34WW1+pmHnV6GWsk6czBAAgAAAAAAAAACAAAAAAAAA3qxAommvdA0ViIFU4Jg24IVDFJiyfIAQAAAAAAAAAAOaCAQAAAAAAvKhqYQGC3wOxmBYxGXTUcKPFhIct7GEEAAAAAAAAAACJDgIAAAAAAOuTMOEKwukDy1uxg8Iz1V06Gp6Ipn1iBAAAAAAAAAAAc7EAAAAAAADGT3tCkv7sAydJgymdUtIiH0ZLV0DRBgQDAAAAAAAAAAMAAAAAAAAA8zqGKJFD9wPJ3liUYYZ4n1I0KjVaHGgEAAAAAAAAAAAAQAAAAAAAAGuF2T7au/sDFR5BHRNxj7qfWXbqPitXBKsqAAAAAAAAqyoAAAAAAABrhdk+2rv7A3N/6Ll627GEovyumItdMAQAAAAAAAAAAABAAAAAAAAAa4XZPtq7+wOutJwq7IXyus1+nbUON3UEACAAAAAAAAAAIAAAAAAAAHAS+/1f7P4Dm5SLTnAqx5rJs7wRTTVcBAAAAAAAAAAAqwAAAAAAAAAAmgsRUWwHBCVh4Gz5U7DU7C4YbIO1wQQAAAAAAAAAAABAAAAAAAAAhxbcZQ9kDwT0jfw5YrRe0Ba80mIjRvAEVQAAAAAAAABVAAAAAAAAAJst3wEgexwEj+ftEr5A+3Q3OQoK23L8BIEBAAAAAAAAgQEAAAAAAACbLd8BIHscBJkjNiPvBGXGbiHBB2MY2gSNAAAAAAAAAKUAAAAAAAAAIVDFJiyfIAQTiaIjlg0qkTB8e2yYSPwEAAAAAAAAAADmggEAAAAAALOOStYcMyEEst8r1bRBqSAItebp/ZWiBAAAAAAAAAAAmhkAAAAAAACzjkrWHDMhBLmaSSHgmwmhGMkyM3lbtwQAAAAAAAAAAAAgAAAAAAAAN93SNL5dIwRpL4zajxSAZUDHwKgr0YsEAAAAAAAAAABJAgAAAAAAAKL8rpiLXTAEwEi+tW0ykIrMMopBB0D2BAAAAAAAAAAAAEAAAAAAAABqtPBFOHYxBAQcomjSQqrmYZVHI6NkoQQAAAAAAAAAAJoBAAAAAAAAFQ3bUpcKNQR2QKlGCfVv1+yGyYjvHIEEAAAAAAAAAACYAAAAAAAAACVhEnQ9gzcEtmOZvFfQUXflN2ig0jTxBAAAAAAAAAAAQAAAAAAAAAAMakdJFjhIBJhvjcbK/INx6Rae7sE0xQQAAAAAAAAAAOkCAAAAAAAAzQc7rpxWVASmiklCJ6fx+hPWB8vQ7fkEYgAAAAAAAABiAAAAAAAAAOFbb9UUeVgEo34WW1+pmHk3i1wl5oOgBAAAAAAAAAAAqwoAAAAAAADJs7wRTTVcBD34B2/OU+iOWg2ci4S7lAQAAAAAAAAAAKsAAAAAAAAAo8WEhy3sYQQsTzgSyRh/uW2uCgWZydEEAAAAAAAAAACJDgIAAAAAADoanoimfWIEEFb+ZonwjIU9Son5FSaDBAAAAAAAAAAAc7EAAAAAAADv5BZSpbRkBE8HsHcee4zPYs2jg4GUzAQAAAAAAAAAAAAgAAAAAAAAUjQqNVocaARmnFfrs3P8r8OLlV5EjOEEAAAAAAAAAAAAQAAAAAAAAPB7uWfj2mgE24Usp2X26F0QypLKhQzcBAAAAAAAAAAACgAAAAAAAAC5lYIfD/ttBGuNQNcZpBjw+rr/xKygtQQAAAAAAAAAAJoBAAAAAAAADTyIGhMwfAQmMEU+cn+chdNsM1U4EPYEAAAAAAAAAAACAAAAAAAAAOyGyYjvHIEEqUoX55J59E0v4WUa918yBZgAAAAAAAAAmAAAAAAAAAA9Son5FSaDBFfTNiQ8eTh/1IoYQQDSMgUAAAAAAAAAAKugAAAAAAAAPUqJ+RUmgwSBvlqh4WBLFUJRA2W/uBEFAAAAAAAAAAABAAAAAAAAAD1KifkVJoMEADmR0+9AFlIMYU62Ao9eBQAAAAAAAAAAjwEAAAAAAAA9Son5FSaDBIuXUygpFoUFIGtTYjaIWgUAAAAAAAAAADgPAAAAAAAAQMfAqCvRiwS228ObuagKNfMlGr4kADUFAAAAAAAAAABJAgAAAAAAAFoNnIuEu5QEhxFhaZkCBnmUvZabFAd5BQAAAAAAAAAAqwAAAAAAAAA3i1wl5oOgBEtzTwiKjQMV4GhUIclvZgWrCgAAAAAAAKsKAAAAAAAAYZVHI6NkoQTJ3liUYYZ4n5VAzemREG8FAAAAAAAAAACaAQAAAAAAAAi15un9laIENjI7JyTxWW+WfZ9ZUjgjBZoZAAAAAAAAmhkAAAAAAAD6uv/ErKC1BHfVTCsjZ4fpIjGgbTpcbAUAAAAAAAAAAJoBAAAAAAAAGMkyM3lbtwSy3yvVtEGpIPpWDXhIIE8FAAAAAAAAAAAAIAAAAAAAAOwuGGyDtcEE2oHn+pzXU6i5V3w0FbsPBQAAAAAAAAAAAEAAAAAAAADpFp7uwTTFBJhvjcbK/INxFa4LlNv6bAUAAAAAAAAAAOkCAAAAAAAAYs2jg4GUzASBlEA+4GT1S7nWgiANaGYFAAAAAAAAAAAAIAAAAAAAAG2uCgWZydEE5lst2nIIqXBt8dozhMtGBQAAAAAAAAAA9HgAAAAAAABtrgoFmcnRBFC9ude2EilujD9n5wNLRgUAAAAAAAAAAJWVAQAAAAAAbiHBB2MY2gQmMEU+cn+chYjje7Zbp30FAAAAAAAAAAAYAAAAAAAAABDKksqFDNwEY+gWvXbG1WnyTOpE2z59BQAAAAAAAAAACgAAAAAAAADDi5VeRIzhBGuNQNcZpBjwBDPTlznzSgUAAAAAAAAAAABAAAAAAAAA5TdooNI08QSmQ0CHYw7IblqViVHhfmgFAAAAAAAAAABAAAAAAAAAANNsM1U4EPYEcsXvnGUWmaxptaPViuRbBQIAAAAAAAAAAgAAAAAAAADMMopBB0D2BMVPi37KLCqYdpqzWI37YwUAAAAAAAAAAABAAAAAAAAAMHx7bJhI/AQB63+sE8rWFwc4ITql7yUFAAAAAAAAAADmggEAAAAAALlXfDQVuw8FsEzFwSycq8go0T1GR179BQAAAAAAAAAAAEAAAAAAAABCUQNlv7gRBeV3wW4ILd3lD30ZG3q17gUAAAAAAAAAAAEAAAAAAAAABzghOqXvJQUEDWVNOEGsWX1D2HkYToYFAAAAAAAAAAArVgAAAAAAAAc4ITql7yUFAZMCTbZVcZ7BdsGxULa0BQAAAAAAAAAAP2sAAAAAAAAHOCE6pe8lBXH+xbv30EVRJqTz7Fz5zAUWawAAAAAAABZrAAAAAAAABzghOqXvJQVrF1/PDZl5PxwW8PxHHbgFVVUAAAAAAACMVQAAAAAAAAc4ITql7yUFuw20YjmvCZL5kxdjsdKNBQAAAAAAAAAA2gAAAAAAAADUihhBANIyBe4nijZDZWxiNBuimRfdgQUAAAAAAAAAAKugAAAAAAAA8yUaviQANQVph0EGqXbflsivM9g1o6kFAAAAAAAAAABJAgAAAAAAAIw/Z+cDS0YFfLkgt7XukjYti+AlrvCABQAAAAAAAAAAlZUBAAAAAABt8dozhMtGBQGTAk22VXGeqNvgKZLq6gUAAAAAAAAAADQAAAAAAAAAbfHaM4TLRgW7DbRiOa8JkidhUkNjWpAFAAAAAAAAAAAbMgAAAAAAAG3x2jOEy0YFGV7NEcF71aNkcc0f4qvGBQAAAAAAAAAApUYAAAAAAAAEM9OXOfNKBXfVTCsjZ4fp70+XdepiigUAAAAAAAAAAABAAAAAAAAA+lYNeEggTwVu9DzXcMJ4ny10I3qGIPIFAAAAAAAAAAAAIAAAAAAAACBrU2I2iFoFOBgGXpz5Byww6b5pJzf4BQAAAAAAAAAAVQAAAAAAAAAga1NiNohaBe4nijZDZWxiPnm1jGrEwAUAAAAAAAAAAOMOAAAAAAAADGFOtgKPXgWnH+tnsLvAMiXN6YepMtwFAAAAAAAAAACPAQAAAAAAAHaas1iN+2MFh766nskv3TUMgfsk7n/vBQBAAAAAAAAAAEAAAAAAAAC51oIgDWhmBRAqxGxAs5XfSJcW3NxIywUAAAAAAAAAAAAgAAAAAAAAWpWJUeF+aAWtb2sJp7WTQNyJMcuTvvAFAAAAAAAAAABAAAAAAAAAACIxoG06XGwFqWcnm7yl+j9mBvtprlnGBQAAAAAAAAAAmgEAAAAAAAAVrguU2/psBZhvjcbK/INxW1t7dfbG0QUAAAAAAAAAAOkCAAAAAAAAlUDN6ZEQbwVmnFfrs3P8rwtIVIS3ufkFAAAAAAAAAACaAQAAAAAAAJS9lpsUB3kF+ErEgVKLIpyvvbLKi668BasAAAAAAAAAqwAAAAAAAADyTOpE2z59BVam1reBYa7lKfqtdl760QUAAAAAAAAAAAoAAAAAAAAAiON7tlunfQVyxe+cZRaZrDNO30kyJugFDAAAAAAAAAAYAAAAAAAAAC2L4CWu8IAFjdjHwMiNDCJ7/aMHu306BgAAAAAAAAAAhD0AAAAAAAAti+AlrvCABRK5UrhZh7COKtIoL53EcAYAAAAAAAAAAGatAAAAAAAALYvgJa7wgAVLozmJfs8Ww5ZpaxvGQE0GAAAAAAAAAACrqgAAAAAAADQbopkX3YEFdB3UMdXNkcE/3/+MO9MjBgAAAAAAAAAAAKAAAAAAAAA0G6KZF92BBZb983FHOsXNXB16eY7iFwYAAAAAAAAAAKsAAAAAAAAAfUPYeRhOhgXOpvz7GkSCi4yRuxyCqS8GKAAAAAAAAAAoAAAAAAAAAH1D2HkYToYF0Fs3gn1wpktBZCKDNHowBgAAAAAAAAAA7lUAAAAAAAB9Q9h5GE6GBVOTjrikEoxPxiXGIe1UBQYVAAAAAAAAABUAAAAAAAAA70+XdepiigWpZyebvKX6P8yQANbUN28GAAAAAAAAAAAAQAAAAAAAAPmTF2Ox0o0FeEQPvddLw1iBc7h0kOIpBgAAAAAAAAAAqwAAAAAAAAD5kxdjsdKNBXa9OyQiw6yq7U9qmARtUQYAAAAAAAAAAC8AAAAAAAAAJ2FSQ2NakAV4RA+910vDWOvHvZ/CYmsGAAAAAAAAAAAbMgAAAAAAAMivM9g1o6kFCNzc5O5P9k8kaN2GioAjBgAAAAAAAAAASQIAAAAAAADBdsGxULa0BXf4vSNDUnvp+CnkV4HdbwYAAAAAAAAAAAQAAAAAAAAAwXbBsVC2tAXFHoEuYjXMJk0riKJnZzcGAAAAAAAAAAA7awAAAAAAABwW8PxHHbgF5m1Cq4Hv5FNNHaAcX35xBgAAAAAAAAAANwAAAAAAAAA+ebWMasTABeiK7Uq20DNevY+33BTFJwYAAAAAAAAAAKsAAAAAAAAAPnm1jGrEwAURdwUD27li1C9f5Xf6vl4GAAAAAAAAAAAcBwAAAAAAAD55tYxqxMAFOBgGXpz5ByxxXOijdZwNBgAAAAAAAAAAHAcAAAAAAABmBvtprlnGBQQcomjSQqrmrjmCX3faMAYAAAAAAAAAAJoBAAAAAAAAZHHNH+KrxgU7tksjWy+HNvL6QZpi2EYGAAAAAAAAAAClBgAAAAAAAGRxzR/iq8YFUcLOkyK4IFz7jVTde3QDBgBAAAAAAAAAAEAAAAAAAABIlxbc3EjLBW3YQrh988ymvwa84K0aMgYAAAAAAAAAAAAgAAAAAAAAW1t7dfbG0QWYb43GyvyDcespAtaTQTkGAAAAAAAAAADpAgAAAAAAACn6rXZe+tEFFAk5soYt4vXApGzYEoR2BgAAAAAAAAAACgAAAAAAAAAlzemHqTLcBc0CINfCvrh0BmACZKjuDQYAAAAAAAAAACsAAAAAAAAAJc3ph6ky3AUG0+2ggNNRcOmHwlSCIAUGAAAAAAAAAABkAQAAAAAAADNO30kyJugFBwteAIuDDaxVVKzwpkYSBgwAAAAAAAAADAAAAAAAAACo2+ApkurqBcUegS5iNcwmj49C5gbgIgYAAAAAAAAAADAAAAAAAAAAqNvgKZLq6gV3+L0jQ1J76eoSLDo2GlYGAAAAAAAAAAAEAAAAAAAAAA99GRt6te4FBCHemtAYrXc3XgyaK1U5BgAAAAAAAAAAAQAAAAAAAADciTHLk77wBRr+3UaIAWtDfbabd5cQZAYAAAAAAAAAAEAAAAAAAAAALXQjeoYg8gWkXByuACZWU1+dYxRd408GACAAAAAAAAAAIAAAAAAAADDpvmknN/gFwDIn15sW0pe+gEwJ9egGBgAAAAAAAAAAVQAAAAAAAAALSFSEt7n5BWuNQNcZpBjwNPzKKWt6EwYAAAAAAAAAAJoBAAAAAAAAKNE9Rkde/QUlYeBs+VOw1EI4ID6wOT0GAAAAAAAAAAAAQAAAAAAAAOmHwlSCIAUG2mZgVPzgmRK2VPC6AMDOBgAAAAAAAAAAZAEAAAAAAAC+gEwJ9egGBi+s7YgHL3v2S8noUBJKtgZVAAAAAAAAAFUAAAAAAAAAcVzoo3WcDQZnyQNPwDwFFNPXRE8mArIGAAAAAAAAAAAcBwAAAAAAAAZgAmSo7g0GVWxFi/Rw892msnzgaLbABgAAAAAAAAAAKwAAAAAAAAA0/Mopa3oTBnfVTCsjZ4fp40oSW8sk/QYAAAAAAAAAAJoBAAAAAAAAXB16eY7iFwZ0HdQx1c2RwSL8qndbVOUGAAAAAAAAAACrAAAAAAAAAI+PQuYG4CIG30cB6J0qNIYAy59Mv3ONBgAAAAAAAAAAMAAAAAAAAAAkaN2GioAjBiJWzXtIJ3Eb0geAaOVmkAYAAAAAAAAAAEkCAAAAAAAAP9//jDvTIwa4wTFgPiVqXEBfk1sBDJsGAAAAAAAAAAAAIAAAAAAAAD/f/4w70yMGEQlPidk2qIDoJwJX3FXGBgAAAAAAAAAAAIAAAAAAAAC9j7fcFMUnBnQd1DHVzZHBiaKyBBjSrwYAAAAAAAAAAKsAAAAAAAAAgXO4dJDiKQYznk7U9cAkydYHEKWtldwGAAAAAAAAAACrAAAAAAAAAEFkIoM0ejAGGTyGyxPnTcSqvI4b3TylBgAAAAAAAAAA7lUAAAAAAACuOYJfd9owBsneWJRhhnifjjyQLdgNwAYAAAAAAAAAAJoBAAAAAAAAvwa84K0aMga1InxN+g8MB9AVyaLBgeoGAAAAAAAAAAAAIAAAAAAAAE0riKJnZzcG30cB6J0qNIYtL/xK2wXQBgAAAAAAAAAAkAAAAAAAAABNK4iiZ2c3BlzZ14+h/xuPNurxZbeuowYAAAAAAAAAAKtqAAAAAAAA6ykC1pNBOQaYb43GyvyDcWg2nMPq7ewGAAAAAAAAAADpAgAAAAAAADdeDJorVTkGJjpv6rz8iCg4k8iePnO7BgAAAAAAAAAAAQAAAAAAAAB7/aMHu306Bib3WRNj/VQOgQomYtnilQY2AAAAAAAAAIQ9AAAAAAAAQjggPrA5PQbagef6nNdTqB6Q5LKoObQGAAAAAAAAAAAAQAAAAAAAAPL6QZpi2EYGkWNjtyeGAmnLN9QrlEDFBgAAAAAAAAAApQYAAAAAAACWaWsbxkBNBtT783+qlQIYQebxEsPs8gYAAAAAAAAAAKuqAAAAAAAA7U9qmARtUQbWGa1QDNwIU/mIES0F17AGAAAAAAAAAAAvAAAAAAAAAOoSLDo2GlYGEk8HGlEjjnghpSTFqyraBgAAAAAAAAAABAAAAAAAAAAvX+V3+r5eBobJTJnHlNe3irMUKvV9wAYAAAAAAAAAABwHAAAAAAAAfbabd5cQZAa2Y5m8V9BRdy2B4jLfYtAGQAAAAAAAAABAAAAAAAAAAOvHvZ/CYmsGM55O1PXAJMmngzplQkSTBgAAAAAAAAAAGzIAAAAAAADMkADW1DdvBgQcomjSQqrm7vSEPxitnQYAAAAAAAAAAABAAAAAAAAA+CnkV4HdbwYSTwcaUSOOeMr4CcWbS5YGAAAAAAAAAAAEAAAAAAAAACrSKC+dxHAGfmAFANfFpdI9O3ZjN8fRBgAAAAAAAAAAEZgAAAAAAAAq0igvncRwBozjCXS65Vef0PRYQDMg9gYAAAAAAAAAAFUVAAAAAAAATR2gHF9+cQZLEsaPYcWEX+pkS2NerNwGNwAAAAAAAAA3AAAAAAAAAMCkbNgShHYGUyTpN/63iZi0D2RLOoqUBgoAAAAAAAAACgAAAAAAAAAAy59Mv3ONBlqA4pT59JarE8ju2J1kDQcAAAAAAAAAADAAAAAAAAAA0geAaOVmkAZ6r1KnWG8MDiZaT6gAPRYHAAAAAAAAAABJAgAAAAAAAKeDOmVCRJMGc9TFdLUrT2pgMLH6RBx6BwAAAAAAAAAAGzIAAAAAAACBCiZi2eKVBpcMX2ZSavfA1Pk/zv69fgdJIgAAAAAAABYvAAAAAAAAgQomYtnilQYdDypgLvNXoPJ3zkV5yR4HAAAAAAAAAAA4DgAAAAAAAMr4CcWbS5YGIlElmeQ/7FkPmD+1IyZaBwAAAAAAAAAABAAAAAAAAABAX5NbAQybBi+s7YgHL3v2xN2SZZWNDgcAIAAAAAAAAAAgAAAAAAAA7vSEPxitnQbJ3liUYYZ4n6NLa94cnFEHAAAAAAAAAAAAQAAAAAAAADbq8WW3rqMGiC+KiUHiLbMlYWsq6nhjBwAAAAAAAAAAq2oAAAAAAACqvI4b3TylBvGUZOnCfnNKJ/z+UTQregcAAAAAAAAAAO5VAAAAAAAAiaKyBBjSrwa4wTFgPiVqXNyO9wISX0oHAAAAAAAAAACrAAAAAAAAAPmIES0F17AGbjWQFlwRN6xDFm+524QiBwAAAAAAAAAADQAAAAAAAAD5iBEtBdewBn5m5Qbb6emVL7fb4linIQcAAAAAAAAAACIAAAAAAAAA09dETyYCsgaGyUyZx5TXt2oq2XqRDnUHAAAAAAAAAAAcBwAAAAAAAB6Q5LKoObQGsEzFwSycq8gqFyFKbBN8BwAAAAAAAAAAAEAAAAAAAAA4k8iePnO7Bqi9JvG7qRyl8T39+/O/PAcAAAAAAAAAAAEAAAAAAAAAjjyQLdgNwAZmnFfrs3P8rz0CIz0LXHsHAAAAAAAAAACaAQAAAAAAAIqzFCr1fcAGeMQkdQfiFaLeaGSevL5HBwAAAAAAAAAAHAcAAAAAAACmsnzgaLbABlTRBsnw2WvsiWJ0Eoc5TwcAAAAAAAAAACsAAAAAAAAAyzfUK5RAxQZMrxNRn6HXecWn0vZA8BgHAAQAAAAAAAAABAAAAAAAAMs31CuUQMUGzqb8+xpEgotRa7jUw25dB6UCAAAAAAAApQIAAAAAAADoJwJX3FXGBobJTJnHlNe3nESI5L6FQAcAAAAAAAAAAACAAAAAAAAAtlTwugDAzgZTQFL++K+RVBONu0fsM1EHAAAAAAAAAABkAQAAAAAAAC0v/ErbBdAGWoDilPn0lqs//9ulw314BwAAAAAAAAAAkAAAAAAAAAA9O3ZjN8fRBpcMX2ZSavfAYlnv4ngZcQcAAAAAAAAAANmJAAAAAAAAPTt2YzfH0QYdDypgLvNXoFaiTZWfYR0HAAAAAAAAAAA4DgAAAAAAACGlJMWrKtoGIlElmeQ/7FldDG0Fbw5WBwAAAAAAAAAABAAAAAAAAADWBxClrZXcBnPUxXS1K09q1s8OzWECKgcAAAAAAAAAAKsAAAAAAAAAIvyqd1tU5Qa4wTFgPiVqXK/Jbg6IUREHAAAAAAAAAACrAAAAAAAAANAVyaLBgeoGfiFs5fcLt1Q8f6+GA3MOBwAgAAAAAAAAACAAAAAAAABoNpzD6u3sBkSA/YdwkgRXN3kkAzzsYAcAAAAAAAAAAOkCAAAAAAAAQebxEsPs8gaXDF9mUmr3wHBCtYqbqAgHAAAAAAAAAAAAgAAAAAAAAEHm8RLD7PIGHQ8qYC7zV6Dou6LMNQFaB6sqAAAAAAAAqyoAAAAAAADQ9FhAMyD2BnvWVutQqS9uuKVVy6XzNAdVFQAAAAAAAFUVAAAAAAAA40oSW8sk/QapZyebvKX6Px9KF3LnrmQHAAAAAAAAAACaAQAAAAAAAHBCtYqbqAgH8beyuRhUkCmbqdP4USXOBwAAAAAAAAAAAIAAAAAAAAATyO7YnWQNBx6DBGTYyJBZxk4j6sTj3AcAAAAAAAAAADAAAAAAAAAAr8luDohREQf8PWv0dNP+XiiqY0BEXcMHAAAAAAAAAACrAAAAAAAAACZaT6gAPRYH4GmaFNc0t81igCyhtQndB0kCAAAAAAAASQIAAAAAAABWok2Vn2EdB3VUdJwinKb/pfJjylOonAcAAAAAAAAAADgOAAAAAAAA8nfORXnJHgd1VHScIpym/+I9B/Nc7bgHAAAAAAAAAAA4DgAAAAAAAC+32+JYpyEHnKBQMxZuzb+MwtMUeBjUBwAAAAAAAAAAIgAAAAAAAABDFm+524QiBza1URQD0cxIjifTHVXe2gcNAAAAAAAAAA0AAAAAAAAA1s8OzWECKgdVXQQbde6iZ/mJfNilf+AHqwAAAAAAAACrAAAAAAAAAPE9/fvzvzwHJoC8rc4khuL+357WhEWpBwAAAAAAAAAAAQAAAAAAAACcRIjkvoVAB3jEJHUH4hWiupPyrwVt5QcAAAAAAAAAAACAAAAAAAAA3mhknry+RwfU9pT1R4dAUCbY/0x984sHAAAAAAAAAAAcBwAAAAAAANyO9wISX0oH/D1r9HTT/l7C3j+/oYmqBwAAAAAAAAAAqwAAAAAAAACJYnQShzlPB3mIgKkgBxhSLlwZGA8w+AcAAAAAAAAAACsAAAAAAAAAE427R+wzUQffvWLUu3glZtYyS4jNl6QHAAAAAAAAAABkAQAAAAAAAKNLa94cnFEHZpxX67Nz/K+5dmtpoG3+BwAAAAAAAAAAAEAAAAAAAABdDG0Fbw5WB9geAv+xLVozRib/pL/VuwcAAAAAAAAAAAQAAAAAAAAAD5g/tSMmWgfYHgL/sS1aM23whoEwwpUHAAAAAAAAAAAEAAAAAAAAADd5JAM87GAH9I38OWK0XtA10sR8qnmsB+kCAAAAAAAA6QIAAAAAAAAlYWsq6nhjB5TPAw1cwBodavrSOEPbqAcAAAAAAAAAAKtqAAAAAAAAH0oXcueuZAcEHKJo0kKq5u+6MwCuWcAHAAAAAAAAAACaAQAAAAAAAGJZ7+J4GXEH8beyuRhUkCmDu7622+GDBwAAAAAAAAAA2YkAAAAAAABqKtl6kQ51B3jEJHUH4hWiRoDBt6JgggcAAAAAAAAAABwHAAAAAAAAP//bpcN9eAcegwRk2MiQWYlxfFyzQ5MHAAAAAAAAAACQAAAAAAAAAGAwsfpEHHoHVV0EG3XuomfJAwMVEdb8B6oVAAAAAAAAGzIAAAAAAAAn/P5RNCt6By2Jv/gqaHzf/Kc1gWDCmgcAAAAAAAAAAO5VAAAAAAAAPQIjPQtcewdrjUDXGaQY8AyqrAya16wHAAAAAAAAAACaAQAAAAAAACoXIUpsE3wHJWHgbPlTsNSbxz80wrHEBwAAAAAAAAAAAEAAAAAAAADU+T/O/r1+B/G3srkYVJApO9RC29rv+AcAAAAAAAAAAM0MAAAAAAAARoDBt6JgggfU9pT1R4dAUB1WrIjbUlQIAAAAAAAAAAAcBwAAAAAAAIO7vrbb4YMHXaVARkiI/d5dWVj+kCtZCAAAAAAAAAAA2YkAAAAAAAAm2P9MffOLB6DhOK0AGp0tLXj9/KytbwgAAAAAAAAAABwHAAAAAAAAiXF8XLNDkwdjj9R7ZMJj7ehueF4j2DQIAAAAAAAAAACQAAAAAAAAAG3whoEwwpUH1LRVPKmW+20zG5tRAaBLCAAAAAAAAAAABAAAAAAAAAD8pzWBYMKaB1rqwneTMwI8x1LLCCs+eAgAAAAAAAAAAO5VAAAAAAAApfJjylOonAc4qEfwhBH1AB1HwNcqAnEIOA4AAAAAAAA4DgAAAAAAANYyS4jNl6QHi4qmaY8UjYd55yzEC94FCAAAAAAAAAAAZAEAAAAAAABq+tI4Q9uoBwNOxSAFwn4CrOabO8a2PwgAAAAAAAAAAKtqAAAAAAAA/t+e1oRFqQfKbBdBP6n3Z4B3E6NMkTgIAAAAAAAAAAABAAAAAAAAAMLeP7+hiaoHf9bo3OdWCzFVVqGnzJgeCAAAAAAAAAAAqwAAAAAAAAAMqqwMmtesB8ecWyL6YI28PobYxafMRAgAAAAAAAAAAJoBAAAAAAAA4j0H81ztuAc4qEfwhBH1ADFVuRcfo00IOA4AAAAAAAA4DgAAAAAAAEYm/6S/1bsH1LRVPKmW+21kLLODist2CAAAAAAAAAAABAAAAAAAAADvujMArlnAB8neWJRhhnifKsK7JCHoNwgAAAAAAAAAAJoBAAAAAAAAKKpjQERdwwd/1ujc51YLMXy2lMoT03AIAAAAAAAAAACrAAAAAAAAAJvHPzTCscQH2oHn+pzXU6ifjuk/BixICAAAAAAAAAAAAEAAAAAAAACbqdP4USXOB12lQEZIiP3eR5f8ocLCMQgAAAAAAAAAAACAAAAAAAAAjMLTFHgY1AduNZAWXBE3rFKg4la1bTgIAAAAAAAAAAAFAAAAAAAAAIzC0xR4GNQHfmblBtvp6ZX5f/m9uCp0CAAAAAAAAAAAEAAAAAAAAACMwtMUeBjUB/PaD4K14153jpjc3eaYGwgNAAAAAAAAAA0AAAAAAAAAxk4j6sTj3Adjj9R7ZMJj7Y1P9p25/nAIAAAAAAAAAAAwAAAAAAAAALqT8q8FbeUH1PaU9UeHQFBQ8SDHx2dACAAAAAAAAAAAAIAAAAAAAAAuXBkYDzD4Bzs3r0BrWXZPGhQfp+YGMwgAAAAAAAAAACsAAAAAAAAAO9RC29rv+AddpUBGSIj93iAE2V1Ixy8IAAAAAAAAAADNDAAAAAAAAMkDAxUR1vwHiC+KiUHiLbMwjJUM0BR/CAAAAAAAAAAAcRwAAAAAAAC5dmtpoG3+B2uNQNcZpBjwUlusy5pjBAgAAAAAAAAAAABAAAAAAAAAUlusy5pjBAjHnFsi+mCNvD598jd7B6gIAAAAAAAAAAAAQAAAAAAAAHnnLMQL3gUIokDok62/+YhqROtoWL+hCAAAAAAAAAAAZAEAAAAAAABVVqGnzJgeCH/W6NznVgsxUWVQNxxuhwgAAAAAAAAAAKsAAAAAAAAAIATZXUjHLwhdJGt6HfUCJu6ifk2HxsUIzQwAAAAAAADNDAAAAAAAAEeX/KHCwjEIXSRreh31AibgJHY82EWcCAAAAAAAAAAAAIAAAAAAAAAaFB+n5gYzCFXnY+yYAHj2icchWu7u8AgAAAAAAAAAACsAAAAAAAAA6G54XiPYNAi5xuaiwYlLwJGEGhreQ8EIAAAAAAAAAACQAAAAAAAAACrCuyQh6DcIZpxX67Nz/K9aLDRekvanCAAAAAAAAAAAmgEAAAAAAABSoOJWtW04CDa1URQD0cxIN5VCJeU98wgFAAAAAAAAAAUAAAAAAAAAgHcTo0yROAjVEUE8eRoiKW745/kvTsIIAAAAAAAAAAABAAAAAAAAAKzmmzvGtj8I5wCUshfjhNQR49Ip/6S5CAAAAAAAAAAAq2oAAAAAAABQ8SDHx2dACKDhOK0AGp0tHekAtfD/nAgAAAAAAAAAAACAAAAAAAAAPobYxafMRAgaw9OpEfSu6SwtfRsFfqcIAAAAAAAAAACaAQAAAAAAAJ+O6T8GLEgIsEzFwSycq8izfUh46329CAAAAAAAAAAAAEAAAAAAAAAzG5tRAaBLCJw7h/mZAFwlfufieTiu3ggEAAAAAAAAAAQAAAAAAAAAHVasiNtSVAig4TitABqdLRPJAjbb6asIAAAAAAAAAAAcBwAAAAAAAF1ZWP6QK1kIXSRreh31AiblkNwV3tP6CNkJAAAAAAAA2YkAAAAAAAAteP38rK1vCBY5Ay9dUaVltJ/F2Lpk9QgAAAAAAAAAABwHAAAAAAAAfLaUyhPTcAh/1ujc51YLMUcM6mK5s4MIAAAAAAAAAACrAAAAAAAAAI1P9p25/nAIucbmosGJS8DgWdoxdqCsCAAAAAAAAAAAMAAAAAAAAAD5f/m9uCp0CJygUDMWbs2/us0actTb2QgAAAAAAAAAABAAAAAAAAAAZCyzg4rLdghqGXgwVfWvHfSCJGvfK/QIBAAAAAAAAAAEAAAAAAAAAMdSywgrPngIWurCd5MzAjxcUrb4OkirCAAAAAAAAAAA7lUAAAAAAAAwjJUM0BR/CJTPAw1cwBod4hdMJmuZxggAAAAAAAAAAHEcAAAAAAAARwzqYrmzgwh/1ujc51YLMd0+/1HDQQ8JAAAAAAAAAACrAAAAAAAAAFFlUDccbocIf9bo3OdWCzHtetvwuhYxCQAAAAAAAAAAqwAAAAAAAADgJHY82EWcCLbGpgio6cBeduH9j2nmIwkAAAAAAAAAAACAAAAAAAAAHekAtfD/nAje8ar5PLgNF/dcfsHizx4JAAAAAAAAAAAAgAAAAAAAAGpE62hYv6EIIHjVbpn00kAMyUIR4v9aCQAAAAAAAAAAZAEAAAAAAAAsLX0bBX6nCAQcomjSQqrmBwgaj75VNgkAAAAAAAAAAJoBAAAAAAAAWiw0XpL2pwhrjUDXGaQY8NVXk+uySlQJAAAAAAAAAACaAQAAAAAAAD598jd7B6gIGsPTqRH0runHZ63LBu49CQAAAAAAAAAAAEAAAAAAAABcUrb4OkirCH1JIRcgJHV43Tu7xGkKEgkAAAAAAAAAAO5VAAAAAAAAE8kCNtvpqwje8ar5PLgNFxrNP2/x62oJAAAAAAAAAAAcBwAAAAAAAOBZ2jF2oKwIYH1HQVFuergbulH+SoJMCTAAAAAAAAAAMAAAAAAAAAAR49Ip/6S5CANOxSAFwn4C2xFeabz3TwkAAAAAAAAAAKtqAAAAAAAAs31IeOt9vQglYeBs+VOw1B9/Jtx4EhUJAAAAAAAAAAAAQAAAAAAAAJGEGhreQ8EIYH1HQVFueridVQ9fU6M8CZAAAAAAAAAAkAAAAAAAAABu+Of5L07CCNlFnjTDimxdr8JyS7JoIQkBAAAAAAAAAAEAAAAAAAAA4hdMJmuZxgh5UzMudt+ILcJhvRGn5wQJAAAAAAAAAABxHAAAAAAAALrNGnLU29kIbjWQFlwRN6xD5/9dbWROCQAAAAAAAAAADAAAAAAAAAC6zRpy1NvZCLcyqdVb6kowRTYoWOwaTgkEAAAAAAAAAAQAAAAAAAAAicchWu7u8Ag+jQWbp4LTpEUtj2iY43EJAAAAAAAAAAArAAAAAAAAALSfxdi6ZPUIoOE4rQAanS0qsaKw2VpICQAAAAAAAAAAHAcAAAAAAADlkNwV3tP6CE2rVs36YhM3fBZPQgtiJgkAAAAAAAAAAACAAAAAAAAAwmG9EafnBAn9YAu3/wRquRsR1xpsv9sJcRwAAAAAAABxHAAAAAAAAN0+/1HDQQ8Jf9bo3OdWCzE45jbDeib7CasAAAAAAAAAqwAAAAAAAADdO7vEaQoSCc6m/PsaRIKLX7JDlEFyrwlMAAAAAAAAAEwAAAAAAAAA3Tu7xGkKEgkZPIbLE+dNxKl56lM4b90JAAAAAAAAAACiVQAAAAAAAB9/Jtx4EhUJ2oHn+pzXU6ihtvliNpKVCQAAAAAAAAAAAEAAAAAAAAD3XH7B4s8eCaDhOK0AGp0tAi91E7NjnwkAAAAAAAAAAACAAAAAAAAAduH9j2nmIwlCm86ucQLlUjHgFTxKBpcJAIAAAAAAAAAAgAAAAAAAAHwWT0ILYiYJQpvOrnEC5VKLVAGAbQ6mCQCAAAAAAAAAAIAAAAAAAADtetvwuhYxCX/W6NznVgsxMRSedMQFzQmrAAAAAAAAAKsAAAAAAAAABwgaj75VNgnJ3liUYYZ4n8ZfczGmppwJAAAAAAAAAACaAQAAAAAAAMdnrcsG7j0JBByiaNJCqub4v4HrIdigCQAAAAAAAAAAAEAAAAAAAAAqsaKw2VpICd7xqvk8uA0XSQZS5TcQzwkAAAAAAAAAABwHAAAAAAAAQ+f/XW1kTgk2tVEUA9HMSK1Udi4iTOcJDAAAAAAAAAAMAAAAAAAAANsRXmm8908JpHQHgk+8hADtHSnHlBf4CQAAAAAAAAAAq2oAAAAAAADVV5PrskpUCcecWyL6YI28WY5V9pNQ9QkAAAAAAAAAAJoBAAAAAAAADMlCEeL/WgkITyOZVHCDnsD33t0mlv4JAAAAAAAAAABkAQAAAAAAABrNP2/x62oJoOE4rQAanS3c3e7P2urECQAAAAAAAAAAHAcAAAAAAABFLY9omONxCRlAgBUdVdwjzZlL2dPzyQkrAAAAAAAAACsAAAAAAAAAobb5YjaSlQmwTMXBLJyryCzLuEP9mVEKAAAAAAAAAAAAQAAAAAAAAMZfczGmppwJZpxX67Nz/K/5gNqXLt8ZCgAAAAAAAAAAmgEAAAAAAAACL3UTs2OfCRY5Ay9dUaVlFqCD1E3UVAoAAAAAAAAAAACAAAAAAAAA+L+B6yHYoAnJ3liUYYZ4nwv8Ov1IJAwKAAAAAAAAAAAAQAAAAAAAANzd7s/a6sQJ3vGq+Ty4DRc0KhjXX05kCgAAAAAAAAAAHAcAAAAAAABJBlLlNxDPCaDhOK0AGp0tw12gO0eBfAoAAAAAAAAAABwHAAAAAAAAqXnqUzhv3QnxlGTpwn5zSi8uJZdbhyUKAAAAAAAAAACiVQAAAAAAAFmOVfaTUPUJGsPTqRH0rumHq9hZSSoiCgAAAAAAAAAAmgEAAAAAAADtHSnHlBf4CdPnXNpTJZF7YSI3kmnoLAoAAAAAAAAAAABAAAAAAAAA7R0px5QX+AkDTsUgBcJ+AuNAPErMeEgKqyoAAAAAAACrKgAAAAAAAMD33t0mlv4JaQV7ueCLd+UlNYR0o+kOCgAAAAAAAAAAgAAAAAAAAADA997dJpb+CWxk++5fxgzj+gGmDLvlbArkAAAAAAAAAOQAAAAAAAAAC/w6/UgkDApmnFfrs3P8r+z0EBXfrIcKAAAAAAAAAAAAQAAAAAAAACU1hHSj6Q4KnDuH+ZkAXCWBOs7iWZOuCoAAAAAAAAAAgAAAAAAAAAD5gNqXLt8ZCmuNQNcZpBjweznTk5ThuAoAAAAAAAAAAJoBAAAAAAAAh6vYWUkqIgoEHKJo0kKq5nSFsrk0640KAAAAAAAAAACaAQAAAAAAAC8uJZdbhyUKLYm/+CpofN8NFx99HzOmCgAAAAAAAAAAolUAAAAAAABhIjeSaegsCvZrq0pabMBN/3uE5F+ZrAoAQAAAAAAAAABAAAAAAAAALMu4Q/2ZUQolYeBs+VOw1NNuINfuZ8AKAAAAAAAAAAAAQAAAAAAAABagg9RN1FQKoOE4rQAanS1LCfbBe3KJCgCAAAAAAAAAAIAAAAAAAAA0KhjXX05kCqDhOK0AGp0tgzulEwr+9woAAAAAAAAAABwHAAAAAAAAw12gO0eBfAoWOQMvXVGlZagNsPEOxJcKAAAAAAAAAAAcBwAAAAAAAOz0EBXfrIcKa41A1xmkGPCBSunWcrYJCwAAAAAAAAAAAEAAAAAAAAB0hbK5NOuNCsneWJRhhnifaQRKi48AeAsAAAAAAAAAAJoBAAAAAAAAqA2w8Q7Elwqg4TitABqdLQ0vwbSxxhwLAAAAAAAAAAAcBwAAAAAAAA0XH30fM6YKWurCd5MzAjxkT9uBBtgXCwAAAAAAAAAAolUAAAAAAAB7OdOTlOG4CsecWyL6YI281KByShleYAsAAAAAAAAAAJoBAAAAAAAA024g1+5nwAragef6nNdTqDhsxTSXGi4LAAAAAAAAAAAAQAAAAAAAAIM7pRMK/vcK3vGq+Ty4DRfwztg/dcszCwAAAAAAAAAAHAcAAAAAAACBSunWcrYJC8ecWyL6YI28znrGks9WygsAAAAAAAAAAABAAAAAAAAAZE/bgQbYFwta6sJ3kzMCPI83Hdiuy8oLAAAAAAAAAABNAAAAAAAAAGRP24EG2BcLuTu2IeUobZyUty/kPyrACwAAAAAAAAAAVVUAAAAAAAANL8G0scYcC97xqvk8uA0XwcN3vA5YpAsAAAAAAAAAABwHAAAAAAAAOGzFNJcaLguwTMXBLJyryAtOFAqyTsELAAAAAAAAAAAAQAAAAAAAAPDO2D91yzMLWxupjdQUTrY5r4gs+cX/CwAAAAAAAAAAHAcAAAAAAADUoHJKGV5gCxrD06kR9K7pK0FMVWJa8gsAAAAAAAAAAJoBAAAAAAAAaQRKi48AeAtmnFfrs3P8rxRffU8T+I8LAAAAAAAAAACaAQAAAAAAABRffU8T+I8La41A1xmkGPBeXwiz2DhIDAAAAAAAAAAAmgEAAAAAAADBw3e8DlikC1sbqY3UFE62IQuHbIFxHgwAAAAAAAAAABwHAAAAAAAAlLcv5D8qwAtYQ5zM/24ThSNFJ4yFMhcMAAAAAAAAAABVVQAAAAAAAAtOFAqyTsELJWHgbPlTsNREiKxFOcdGDAAAAAAAAAAAAEAAAAAAAADOesaSz1bKCxrD06kR9K7prvoMlj+zFwwAAAAAAAAAAABAAAAAAAAAjzcd2K7Lygt9SSEXICR1eE0pfkLmB0oMAAAAAAAAAABNAAAAAAAAACtBTFViWvILBByiaNJCquaIWm/hWHAKDAAAAAAAAAAAmgEAAAAAAAA5r4gs+cX/C69heW9vYsYtBL8Mam1gIAwAAAAAAAAAABwHAAAAAAAAiFpv4VhwCgzJ3liUYYZ4nxg6eY/3iNYMAAAAAAAAAACaAQAAAAAAACNFJ4yFMhcMiUxAWI6j/oJ747A+4aLYDFVVAAAAAAAAVVUAAAAAAACu+gyWP7MXDAQcomjSQqrmFOP8ZOwpiwwAAAAAAAAAAABAAAAAAAAAIQuHbIFxHgyvYXlvb2LGLQ4l27aCe5cMAAAAAAAAAAAcBwAAAAAAAAS/DGptYCAMu2ryihYLY9kyGVC05k2mDAAAAAAAAAAAHAcAAAAAAABEiKxFOcdGDNqB5/qc11OoC1YbT93ajgwAAAAAAAAAAABAAAAAAAAAXl8Is9g4SAx31UwrI2eH6Zc5YAas7qIMAAAAAAAAAACaAQAAAAAAAE0pfkLmB0oMMiqX5B7D9Oc5uJZVu5H2DE0AAAAAAAAATQAAAAAAAAAU4/xk7CmLDMneWJRhhnifmM6EptqoYA0AAAAAAAAAAABAAAAAAAAAC1YbT93ajgywTMXBLJyryLcYRMOgeRgNAAAAAAAAAAAAQAAAAAAAAA4l27aCe5cMu2ryihYLY9lJewbh5PRJDQAAAAAAAAAAHAcAAAAAAACXOWAGrO6iDKlnJ5u8pfo/KEBwy8grNg0AAAAAAAAAAJoBAAAAAAAAMhlQtOZNpgylQYf2J5SLMtn14Ie4iDUNHAcAAAAAAAAcBwAAAAAAABg6eY/3iNYMZpxX67Nz/K8JOfs0U0oADQAAAAAAAAAAmgEAAAAAAAAJOfs0U0oADWuNQNcZpBjwEtq/KvPAkw0AAAAAAAAAAJoBAAAAAAAAtxhEw6B5GA0lYeBs+VOw1OnA3Ps6cscNAAAAAAAAAAAAQAAAAAAAAChAcMvIKzYNBByiaNJCqubZvzfBLtvHDQAAAAAAAAAAmgEAAAAAAABJewbh5PRJDaVBh/YnlIsyqCpCCcKS/w0cBwAAAAAAABwHAAAAAAAAmM6EptqoYA1mnFfrs3P8ryyT7SqdQuANAAAAAAAAAAAAQAAAAAAAABLavyrzwJMNd9VMKyNnh+l+jKNZCW0nDgAAAAAAAAAAmgEAAAAAAADpwNz7OnLHDdqB5/qc11Ooy1Fv2NDlfA4AAAAAAAAAAABAAAAAAAAA2b83wS7bxw3J3liUYYZ4n+IuE5BoWDUOAAAAAAAAAACaAQAAAAAAACyT7SqdQuANa41A1xmkGPD+6/N0hrA8DgAAAAAAAAAAAEAAAAAAAAB+jKNZCW0nDqlnJ5u8pfo/A4wOaQO8ig4AAAAAAAAAAJoBAAAAAAAA4i4TkGhYNQ5mnFfrs3P8r1PDoQFPEcEOAAAAAAAAAACaAQAAAAAAAP7r83SGsDwOd9VMKyNnh+n2Df6wLUreDgAAAAAAAAAAAEAAAAAAAADLUW/Y0OV8DrBMxcEsnKvIkyxNN6hT6Q4AAAAAAAAAAABAAAAAAAAAA4wOaQO8ig4EHKJo0kKq5ugrIODsXC8PAAAAAAAAAACaAQAAAAAAAFPDoQFPEcEOhBhWd+/OdAH+ySoGKW9DDwAAAAAAAAAAmgEAAAAAAAD2Df6wLUreDqlnJ5u8pfo/jxiLRZjxWA8AAAAAAAAAAABAAAAAAAAAkyxNN6hT6Q4lYeBs+VOw1OdHm+oe20wPAAAAAAAAAAAAQAAAAAAAAOgrIODsXC8PGHc/ZH+AGxS8424hZOOLDwAAAAAAAAAAmgEAAAAAAAD+ySoGKW9DDxHY422UFFblcLDVSQMyhg8AAAAAAAAAAJoBAAAAAAAA50eb6h7bTA/agef6nNdTqP4UinLK3oIPAAAAAAAAAAAAQAAAAAAAAI8Yi0WY8VgPBByiaNJCqua+pJX6hjW2DwAAAAAAAAAAAEAAAAAAAAD+FIpyyt6CD7BMxcEsnKvIG5GnMAs2SRAAQAAAAAAAAABAAAAAAAAAcLDVSQMyhg/b5Wp/iJVTbhC1PQCJiTAQmgEAAAAAAACaAQAAAAAAALzjbiFk44sP2+Vqf4iVU27WcC+CjO4lEJoBAAAAAAAAmgEAAAAAAAC+pJX6hjW2D8neWJRhhnifuBgvSfKZRhAAQAAAAAAAAABAAAAAAAAA"; - let resp = "Cv7SAQoFdG90YWwKA24vYQoeazhzLmlvL2FwaS9hdXRvc2NhbGluZy92MS5pbml0Cktnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci4oKmxvZ3NSZXF1ZXN0KS5FeHBvcnQKGXJlZmxlY3QubWFwYXNzaWduX2Zhc3RzdHIKNWdpdGh1Yi5jb20vbXdpdGtvdy9nby1jb25udHJhY2suZGlhbENsaWVudENvbm5UcmFja2VyCi1naXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLmRlY29kZVVpbnQ2NHMKNWdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28uKCpTZXNzaW9uKS51cGxvYWREYXRhChpyZWdleHAuKCpSZWdleHApLmRvRXhlY3V0ZQpGZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL3Byb3RvLigqQmxvY2spLkVuY29kZUNvbHVtbgoYcmVnZXhwLm1ha2VPbmVQYXNzLmZ1bmMxCilnaXRodWIuY29tL3Nub3dmbGFrZWRiL2dvc25vd2ZsYWtlLmluaXQuMwouZ2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS4oKkxpbmUpLmVuY29kZQo4Z2l0aHViLmNvbS9td2l0a293L2dvLWNvbm50cmFjay5OZXdEaWFsQ29udGV4dEZ1bmMuZnVuYzEKS2dpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIuYnVpbGRMYWJlbFNldC5mdW5jMQpPZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3Ivc2VydmljZS90ZWxlbWV0cnkubmV3U2FtcGxlZExvZ2dlci5XcmFwQ29yZS5mdW5jMgobcnVudGltZS9kZWJ1Zy5SZWFkQnVpbGRJbmZvCj9naXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9tb2RlbC9sYWJlbHMuKCpCdWlsZGVyKS5MYWJlbHMKTmdpdGh1Yi5jb20vYXBhY2hlL2Fycm93L2dvL3YxNS9hcnJvdy9jb21wdXRlL2ludGVybmFsL2tlcm5lbHMuR2V0Q29tcGFyZUtlcm5lbAoLZm10LkZwcmludGYKKmdpdGh1Yi5jb20vbWl0Y2hlbGxoL3JlZmxlY3R3YWxrLndhbGtTbGljZQpYZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby9nb2RlbHRhcHJvZi9pbnRlcm5hbC9wcHJvZi4oKnByb2ZpbGVCdWlsZGVyKS5zdHJpbmdJbmRleApBZ2l0aHViLmNvbS9hbGVjdGhvbWFzL3BhcnRpY2lwbGUvdjIuKCpnZW5lcmF0b3JDb250ZXh0KS5wYXJzZVRlcm0KInJlZ2V4cC9zeW50YXguKCpwYXJzZXIpLnBhcnNlQ2xhc3MKLmdvLm9wZW50ZWxlbWV0cnkuaW8vcHJvdG8vb3RscC9tZXRyaWNzL3YxLmluaXQKQGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9wcm90by4oKkJsb2NrKS5BcHBlbmQKNmdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2NvbmZtYXAuTmV3RnJvbVN0cmluZ01hcApMZ2l0aHViLmNvbS9hbGVjdGhvbWFzL3BhcnRpY2lwbGUvdjIuKCpnZW5lcmF0b3JDb250ZXh0KS5wYXJzZVRlcm1Ob01vZGlmaWVycwo7Z2l0aHViLmNvbS9wcm9tZXRoZXVzL2NsaWVudF9nb2xhbmcvcHJvbWV0aGV1cy5NdXN0UmVnaXN0ZXIKMWdpdGh1Yi5jb20va25hZGgva29hbmYvcHJvdmlkZXJzL2NvbmZtYXAuUHJvdmlkZXIKHnJlZ2V4cC9zeW50YXguKCpjb21waWxlcikuaW5zdApHZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby91cHN0cmVhbS9yZW1vdGUuKCpSZW1vdGUpLnVwbG9hZFByb2ZpbGUKEXJ1bnRpbWUuc2NoZWRpbml0ClFnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLmJhdGNoU2FtcGxlc0FuZFRpbWVTZXJpZXMKMmdvLm9wZW50ZWxlbWV0cnkuaW8vb3RlbC9hdHRyaWJ1dGUuY29tcHV0ZURpc3RpbmN0Cj9naXRodWIuY29tL2F3cy9hd3Mtc2RrLWdvLXYyL3NlcnZpY2UvczMvaW50ZXJuYWwvZW5kcG9pbnRzLmluaXQKDWlvLkNvcHlCdWZmZXIKVGdpdGh1Yi5jb20vcHJvbWV0aGV1cy9jbGllbnRfZ29sYW5nL3Byb21ldGhldXMuKCpNZXRyaWNWZWMpLkdldE1ldHJpY1dpdGhMYWJlbFZhbHVlcwpRZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvcHJvY2Vzc29yL3Byb2Nlc3NvcmhlbHBlci5OZXdNZXRyaWNzUHJvY2Vzc29yLmZ1bmMxCmFnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLigqbWV0cmljc0V4cG9ydGVyKS5leHBvcnRTdW1tYXJ5RGF0YVBvaW50CgxydW50aW1lLm1haW4KTGdvb2dsZS5nb2xhbmcub3JnL3Byb3RvYnVmL3JlZmxlY3QvcHJvdG9yZWdpc3RyeS4oKkZpbGVzKS5SZWdpc3RlckZpbGUuZnVuYzIKN2dpdGh1Yi5jb20va2xhdXNwb3N0L2NvbXByZXNzL2ZsYXRlLigqY29tcHJlc3NvcikuY2xvc2UKGGNvbXByZXNzL2ZsYXRlLk5ld1dyaXRlcgo5Z2l0aHViLmNvbS9wcm9tZXRoZXVzL2NsaWVudF9nb2xhbmcvcHJvbWV0aGV1cy5OZXdTdW1tYXJ5ChtydW50aW1lL3Bwcm9mLnByb2ZpbGVXcml0ZXIKSmdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2V4cG9ydGVyL2V4cG9ydGVyaGVscGVyLm5ld1F1ZXVlU2VuZGVyLmZ1bmMxCj1nb29nbGUuZ29sYW5nLm9yZy9wcm90b2J1Zi9pbnRlcm5hbC9maWxlZGVzYy4oKkZpbGUpLmxhenlJbml0ChlyZWdleHAvc3ludGF4LmFwcGVuZFJhbmdlChdjb21wcmVzcy9nemlwLk5ld1JlYWRlcgpjZ2l0aHViLmNvbS9hcGFjaGUvYXJyb3cvZ28vdjE1L2Fycm93L2NvbXB1dGUvaW50ZXJuYWwva2VybmVscy5udW1lcmljQ29tcGFyZUtlcm5lbFtnby5zaGFwZS51aW50MTZdCkBnaXRodWIuY29tL3Byb21ldGhldXMvY2xpZW50X2dvbGFuZy9wcm9tZXRoZXVzLmlubGluZUxhYmVsVmFsdWVzCkRnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9kaXNjb3ZlcnkvZmlsZS5uZXdEaXNjb3ZlcmVyTWV0cmljcwo3Z2l0aHViLmNvbS9hbGVjdGhvbWFzL3BhcnRpY2lwbGUvdjIuKCpzdHJ1Y3RMZXhlcikuTmV4dAouZ2l0aHViLmNvbS9zbm93Zmxha2VkYi9nb3Nub3dmbGFrZS5yZWFkQ0FDZXJ0cwp4Z2l0aHViLmNvbS9vcGVuLXRlbGVtZXRyeS9vcGVudGVsZW1ldHJ5LWNvbGxlY3Rvci1jb250cmliL3JlY2VpdmVyL3Byb21ldGhldXNyZWNlaXZlci9pbnRlcm5hbC4oKm1ldHJpY0ZhbWlseSkuYWRkU2VyaWVzCjBnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvLigqU2Vzc2lvbikucmVzZXQKhQFnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcHJvY2Vzc29yL3Jlc291cmNlZGV0ZWN0aW9ucHJvY2Vzc29yL2ludGVybmFsLigqUmVzb3VyY2VQcm92aWRlcikuR2V0LmZ1bmMxCjdnaXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9mbGF0ZS4oKmZhc3RHZW4pLmFkZEJsb2NrCipnb29nbGUuZ29sYW5nLm9yZy9wcm90b2J1Zi9wcm90by5Vbm1hcnNoYWwKIXJlZ2V4cC9zeW50YXguKCpjb21waWxlcikuY29tcGlsZQqCAWdpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9wcm9jZXNzb3IvcmVzb3VyY2VkZXRlY3Rpb25wcm9jZXNzb3IuKCpyZXNvdXJjZURldGVjdGlvblByb2Nlc3NvcikuU3RhcnQKPGdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28uKCpjcHVQcm9maWxlQ29sbGVjdG9yKS5yZXNldAo1Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NoLWdvL3Byb3RvLkNvbFN0ci5FbmNvZGVDb2x1bW4KQmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpTdHJpbmcpLkVuY29kZQpLZ29vZ2xlLmdvbGFuZy5vcmcvcHJvdG9idWYvaW50ZXJuYWwvaW1wbC4oKk1lc3NhZ2VJbmZvKS5tYWtlS25vd25GaWVsZHNGdW5jCg9yZWdleHAubmV3UXVldWUKJG5ldC9odHRwLigqdHJhbnNmZXJXcml0ZXIpLndyaXRlQm9keQo6Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpiYXRjaCkuY2xvc2VRdWVyeQojZ2l0aHViLmNvbS9rbmFkaC9rb2FuZi9tYXBzLkZsYXR0ZW4KHHJ1bnRpbWUvZGVidWcuUGFyc2VCdWlsZEluZm8KLmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jaC1nby9jb21wcmVzcy5OZXdXcml0ZXIKKWdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3NlcnZpY2UuTmV3ChVieXRlcy4oKkJ1ZmZlcikuV3JpdGUKKWs4cy5pby9hcGltYWNoaW5lcnkvcGtnL3V0aWwvdmVyc2lvbi5pbml0ChNzeW5jLigqT25jZSkuZG9TbG93Cg9zeW5jLigqT25jZSkuRG8KVGdpdGh1Yi5jb20vYXBhY2hlL2Fycm93L2dvL3YxNS9hcnJvdy9jb21wdXRlL2ludGVybmFsL2tlcm5lbHMuY29tcGFyZVRpbWVzdGFtcEtlcm5lbAovZ2l0aHViLmNvbS9hbGVjdGhvbWFzL3BhcnRpY2lwbGUvdjIubmV3VGFnTGV4ZXIKPGdpdGh1Yi5jb20vZ29vZ2xlL3Bwcm9mL3Byb2ZpbGUuKCpQcm9maWxlKS5Xcml0ZVVuY29tcHJlc3NlZAomZ2l0aHViLmNvbS9rbmFkaC9rb2FuZi92Mi4oKktvYW5mKS5SYXcKQ2dpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS4oKnRpbWVMaW1pdEFwcGVuZGVyKS5BcHBlbmQKQmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpTdHJpbmcpLkFwcGVuZAoZcmVnZXhwL3N5bnRheC5hcHBlbmRUYWJsZQo3Z2l0aHViLmNvbS9zaGlyb3UvZ29wc3V0aWwvdjMvaW50ZXJuYWwvY29tbW9uLlJlYWRMaW5lcworZ2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS5pbml0LmZ1bmMzNAozZ2l0aHViLmNvbS9nb29nbGUvZ28tY21wL2NtcC9pbnRlcm5hbC9mdW5jdGlvbi5pbml0CkVnb29nbGUuZ29sYW5nLm9yZy9wcm90b2J1Zi9pbnRlcm5hbC9maWxlZGVzYy4oKk1lc3NhZ2UpLnVubWFyc2hhbEZ1bGwKJmdvLm9wZW5jZW5zdXMuaW8vdHJhY2UvdHJhY2VzdGF0ZS5pbml0CkRnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL3Vwc3RyZWFtL3JlbW90ZS4oKlJlbW90ZSkuc2FmZVVwbG9hZAopZ2l0aHViLmNvbS9zcGYxMy9jb2JyYS4oKkNvbW1hbmQpLkV4ZWN1dGUKImNvbXByZXNzL2d6aXAuKCpSZWFkZXIpLnJlYWRIZWFkZXIKF25ldC4oKlRDUENvbm4pLlJlYWRGcm9tCkhnaXRodWIuY29tL2FsZWN0aG9tYXMvcGFydGljaXBsZS92Mi4oKmdlbmVyYXRvckNvbnRleHQpLnBhcnNlRGlzanVuY3Rpb24KPGdvb2dsZS5nb2xhbmcub3JnL3Byb3RvYnVmL2ludGVybmFsL2ltcGwuKCpNZXNzYWdlSW5mbykuaW5pdAotZ2l0aHViLmNvbS9rbGF1c3Bvc3QvY29tcHJlc3MvZmxhdGUuTmV3V3JpdGVyCjhnaXRodWIuY29tL0NsaWNrSG91c2UvY2gtZ28vcHJvdG8uQ29sVUludDY0LkVuY29kZUNvbHVtbgpQZ2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvZGlzY292ZXJ5L2ZpbGUuKCpTRENvbmZpZykuTmV3RGlzY292ZXJlck1ldHJpY3MKNGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jaC1nby9wcm90by4oKkNvbEludDY0KS5BcHBlbmQKPGdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS4oKnNjcmFwZUxvb3ApLmFwcGVuZAonbWltZS9tdWx0aXBhcnQuKCpXcml0ZXIpLkNyZWF0ZUZvcm1GaWxlCj5nby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9wZGF0YS9wY29tbW9uLk1hcC5FbnN1cmVDYXBhY2l0eQopcmVnZXhwL3N5bnRheC4oKnBhcnNlcikucGFyc2VVbmljb2RlQ2xhc3MKdWdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9jbGlja2hvdXNlcHJvZmlsZWV4cG9ydGVyL2NoLigqY2xpY2tob3VzZUFjY2Vzc05hdGl2ZUNvbHVtbmFyKS5JbnNlcnRCYXRjaAopZ2l0aHViLmNvbS9zaWduYWxmeC9zYXBtLXByb3RvL2dlbi5pbml0LjIKEnJlZ2V4cC5tYWtlT25lUGFzcwo+Y29kZS5jbG91ZGZvdW5kcnkub3JnL2dvLWxvZ2dyZWdhdG9yL3JwYy9sb2dncmVnYXRvcl92Mi5pbml0LjEKe2dpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9yZWNlaXZlci9wcm9tZXRoZXVzcmVjZWl2ZXIvaW50ZXJuYWwuKCptZXRyaWNGYW1pbHkpLmFwcGVuZE1ldHJpYwpjZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci4oKm1ldHJpY3NFeHBvcnRlcikuZXhwb3J0SGlzdG9ncmFtRGF0YVBvaW50Ckhnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9jb25zdW1lci5Db25zdW1lTWV0cmljc0Z1bmMuQ29uc3VtZU1ldHJpY3MKQmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpTdHJpbmcpLkRlY29kZQovZ2l0aHViLmNvbS92bXdhcmUvZ292bW9taS92aW0yNS90eXBlcy5pbml0LjY2NTgKYWdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIuKCptZXRyaWNzRXhwb3J0ZXIpLmV4cG9ydE51bWJlckRhdGFQb2ludHMKI3JlZ2V4cC4oKlJlZ2V4cCkuRmluZEFsbFN0cmluZ0luZGV4CjhnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9zY3JhcGUuTmV3TWFuYWdlci5mdW5jMQohcmVnZXhwL3N5bnRheC4oKnBhcnNlcikubmV3UmVnZXhwCg5yZWdleHAuY29tcGlsZQoPcnVudGltZS5kb0luaXQxChFyZWZsZWN0LnBhY2tFZmFjZQo1Z28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvcGRhdGEvcGNvbW1vbi5NYXAuUmFuZ2UKNWdvLm9wZW50ZWxlbWV0cnkuaW8vb3RlbC9hdHRyaWJ1dGUuTmV3U2V0V2l0aEZpbHRlcmVkClJnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL2dvZGVsdGFwcm9mL2ludGVybmFsL3Bwcm9mLigqcHJvZmlsZUJ1aWxkZXIpLkJ1aWxkCj9naXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9zY3JhcGUuKCpNYW5hZ2VyKS5yZWxvYWQuZnVuYzEKQWdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY2xpY2tob3VzZSkuUHJlcGFyZUJhdGNoCkRnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL3Vwc3RyZWFtL3JlbW90ZS4oKlJlbW90ZSkuaGFuZGxlSm9icwogZ29sYW5nLm9yZy94L25ldC9odG1sLm1hcC5pbml0LjEKOGdpdGh1Yi5jb20vcHJvbWV0aGV1cy9jb21tb24vbW9kZWwubGFiZWxTZXRUb0ZpbmdlcnByaW50CjNnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9zZXJ2aWNlL3RlbGVtZXRyeS5OZXcKKGdpdGh1Yi5jb20va25hZGgva29hbmYvdjIuKCpLb2FuZikuTWVyZ2UKK2dpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLmRpYWwKGGJ1ZmlvLigqV3JpdGVyKS5SZWFkRnJvbQohbmV0L2h0dHAuKCpUcmFuc3BvcnQpLmRpYWxDb25uRm9yCkFnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqQXJyYXkpLmFwcGVuZApTZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby9nb2RlbHRhcHJvZi9pbnRlcm5hbC9wcHJvZi4oKnByb2ZpbGVCdWlsZGVyKS5TYW1wbGUKHnJlZ2V4cC9zeW50YXguKCpjb21waWxlcikucGx1cwopZ28ub3BlbnRlbGVtZXRyeS5pby9vdGVsL2F0dHJpYnV0ZS5OZXdTZXQKT2dvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL290ZWxjb2wuKCpDb2xsZWN0b3IpLnNldHVwQ29uZmlndXJhdGlvbkNvbXBvbmVudHMKQGdvb2dsZS5nb2xhbmcub3JnL3Byb3RvYnVmL2ludGVybmFsL2ltcGwuKCpNZXNzYWdlSW5mbykuaW5pdE9uY2UKRWdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS4oKnNjcmFwZUxvb3ApLnNjcmFwZUFuZFJlcG9ydAo4Z2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvbW9kZWwvbGFiZWxzLk5ld0J1aWxkZXIKIWdpdGh1Yi5jb20vZ29vZ2xlL2dvLWNtcC9jbXAuaW5pdAo8Z2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvZGlzY292ZXJ5LlJlZ2lzdGVyU0RNZXRyaWNzClFnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9jb25mbWFwL2NvbnZlcnRlci9leHBhbmRjb252ZXJ0ZXIuY29udmVydGVyLkNvbnZlcnQKSWdvb2dsZS5nb2xhbmcub3JnL3Byb3RvYnVmL3JlZmxlY3QvcHJvdG9yZWdpc3RyeS5yYW5nZVRvcExldmVsRGVzY3JpcHRvcnMKGHJlZ2V4cC4oKmJpdFN0YXRlKS5yZXNldAo3Z2l0aHViLmNvbS9hbGVjdGhvbWFzL3BhcnRpY2lwbGUvdjIuKCpzdHJ1Y3RMZXhlcikuUGVlawpmZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL2NsaWNraG91c2Vwcm9maWxlZXhwb3J0ZXIuKCpjbGlja2hvdXNlUHJvZmlsZUV4cG9ydGVyKS5zZW5kChJydW50aW1lLnByb2NyZXNpemUKHm5ldC9odHRwLigqVHJhbnNwb3J0KS5kaWFsQ29ubgoxZ2l0aHViLmNvbS9taXRjaGVsbGgvY29weXN0cnVjdHVyZS4oKndhbGtlcikuRXhpdApFZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci5idWlsZExhYmVsU2V0Ciljb21wcmVzcy9mbGF0ZS4oKmh1ZmZtYW5FbmNvZGVyKS5nZW5lcmF0ZQoVcmVnZXhwLmNvbXBpbGVPbmVQYXNzCjJnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLigqTG9jYXRpb24pLmVuY29kZQo5Z2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvc2NyYXBlLigqc2NyYXBlTG9vcCkucnVuCjlnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmNsaWNraG91c2UpLmRpYWwKLmNvbXByZXNzL2ZsYXRlLigqaHVmZm1hbkJpdFdyaXRlcikuaW5kZXhUb2tlbnMKJWdpdGh1Yi5jb20vbWl0Y2hlbGxoL3JlZmxlY3R3YWxrLldhbGsKDWlvLmNvcHlCdWZmZXIKPGs4cy5pby9hcGltYWNoaW5lcnkvcGtnL3J1bnRpbWUuKCpTY2hlbWVCdWlsZGVyKS5BZGRUb1NjaGVtZQoQc3luYy4oKlBvb2wpLlB1dAojZ28udWJlci5vcmcvemFwL3phcGNvcmUubmV3Q291bnRlcnMKdGdpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9yZWNlaXZlci9wcm9tZXRoZXVzcmVjZWl2ZXIvaW50ZXJuYWwuKCp0cmFuc2FjdGlvbikuQXBwZW5kCjFnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9jb25mbWFwLigqQ29uZikuR2V0CkJnb29nbGUuZ29sYW5nLm9yZy9wcm90b2J1Zi9pbnRlcm5hbC9maWxlZGVzYy4oKkZpbGUpLnVubWFyc2hhbEZ1bGwKXGdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2V4cG9ydGVyL2V4cG9ydGVyaGVscGVyLigqbWV0cmljc1NlbmRlcldpdGhPYnNlcnZhYmlsaXR5KS5zZW5kCilnaXRodWIuY29tL3NwZjEzL2NvYnJhLigqQ29tbWFuZCkuZXhlY3V0ZQojZ2l0aHViLmNvbS9rbmFkaC9rb2FuZi9tYXBzLmZsYXR0ZW4KKWVuY29kaW5nL2pzb24uKCplbmNvZGVTdGF0ZSkucmVmbGVjdFZhbHVlCh1lbmNvZGluZy9iaW5hcnkuQXBwZW5kVXZhcmludAodcnVudGltZS9wcHJvZi5TdGFydENQVVByb2ZpbGUKDHJ1bnRpbWUubWFsZwo2Z28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3Ivb3RlbGNvbC4oKkNvbGxlY3RvcikuUnVuChZyZWZsZWN0LigqTWFwSXRlcikuS2V5CidnaXRodWIuY29tL2tuYWRoL2tvYW5mL3YyLigqS29hbmYpLkxvYWQKOmdpdGh1Yi5jb20vYXBhY2hlL2Fycm93L2dvL3YxNS9hcnJvdy9pbnRlcm5hbC9mbGF0YnVmLmluaXQKCmlvLlJlYWRBbGwKMmdpdGh1Yi5jb20vYWxlY3Rob21hcy9wYXJ0aWNpcGxlL3YyLnZhbGlkYXRlLmZ1bmMxCk9nby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci4oKmJhc2VSZXF1ZXN0U2VuZGVyKS5zZW5kChtyZWdleHAuKCpSZWdleHApLmFsbE1hdGNoZXMKLmdpdGh1Yi5jb20va2xhdXNwb3N0L2NvbXByZXNzL2ZsYXRlLm5ld0Zhc3RFbmMKLWdpdGh1Yi5jb20vZ29vZ2xlL3Bwcm9mL3Byb2ZpbGUuZGVjb2RlTWVzc2FnZQpGZ2l0aHViLmNvbS9hcGFjaGUvYXJyb3cvZ28vdjE1L2Fycm93L2NvbXB1dGUuUmVnaXN0ZXJTY2FsYXJDb21wYXJpc29ucwoxZ2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS4oKlByb2ZpbGUpLmVuY29kZQo5Z28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3Ivc2VydmljZS90ZWxlbWV0cnkubmV3TG9nZ2VyChRieXRlcy4oKkJ1ZmZlcikuZ3JvdwoTcmVnZXhwL3N5bnRheC5QYXJzZQo0Y29tcHJlc3MvZmxhdGUuKCpodWZmbWFuQml0V3JpdGVyKS53cml0ZUJsb2NrRHluYW1pYwo5Z28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvcGRhdGEvcGNvbW1vbi5jb3B5Qnl0ZVNsaWNlCkBnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvcHJvdG8uKCpCbG9jaykuRGVjb2RlCjhnaXRodWIuY29tL2luZmx1eGRhdGEvaW5mbHV4ZGItb2JzZXJ2YWJpbGl0eS9jb21tb24uaW5pdAomazhzLmlvL2FwaS9uZXR3b3JraW5nL3YxLmFkZEtub3duVHlwZXMKQWdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpBcnJheSkuQXBwZW5kCixnaXRodWIuY29tL2F3cy9hd3Mtc2RrLWdvL2F3cy9lbmRwb2ludHMuaW5pdApdZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyL3Bwcm9mcGFyc2VyLigqcFByb2ZQYXJzZXIpLlBhcnNlChVyZWdleHAvc3ludGF4LkNvbXBpbGUKhgFnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcmVjZWl2ZXIvcHJvbWV0aGV1c3JlY2VpdmVyL2ludGVybmFsLigqbWV0cmljRmFtaWx5KS5sb2FkTWV0cmljR3JvdXBPckNyZWF0ZQoeY29tcHJlc3MvZmxhdGUuKCpXcml0ZXIpLkNsb3NlCipnaXRodWIuY29tL2tuYWRoL2tvYW5mL3YyLnBvcHVsYXRlS2V5UGFydHMKNGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqYmF0Y2gpLlNlbmQKLWdpdGh1Yi5jb20vZ29sYW5nL3Byb3RvYnVmL3Byb3RvLlJlZ2lzdGVyRmlsZQpLZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvZXhwb3J0ZXIvZXhwb3J0ZXJoZWxwZXIuKCp0aW1lb3V0U2VuZGVyKS5zZW5kCmBnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvcmVjZWl2ZXIvcHlyb3Njb3BlcmVjZWl2ZXIvY29tcHJlc3MuKCpEZWNvbXByZXNzb3IpLkRlY29tcHJlc3MKXmdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vZ29kZWx0YXByb2YvaW50ZXJuYWwvcHByb2YuKCpEZWx0YUhlYXBQcm9maWxlcikuV3JpdGVIZWFwUHJvdG8KHGNyeXB0by94NTA5LnBhcnNlQ2VydGlmaWNhdGUKPGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY29ubmVjdCkuZmlyc3RCbG9jawpFZ2l0aHViLmNvbS9hbGVjdGhvbWFzL3BhcnRpY2lwbGUvdjIuKCpnZW5lcmF0b3JDb250ZXh0KS5zdWJwYXJzZUdyb3VwCltnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci4oKmxvZ3NFeHBvcnRlcldpdGhPYnNlcnZhYmlsaXR5KS5zZW5kCiBnby51YmVyLm9yZy96YXAub3B0aW9uRnVuYy5hcHBseQpiZ2l0aHViLmNvbS9hcGFjaGUvYXJyb3cvZ28vdjE1L2Fycm93L2NvbXB1dGUvaW50ZXJuYWwva2VybmVscy5udW1lcmljQ29tcGFyZUtlcm5lbFtnby5zaGFwZS5pbnQ2NF0KRWdpdGh1Yi5jb20vYXBhY2hlL2Fycm93L2dvL3YxNS9hcnJvdy9jb21wdXRlLlJlZ2lzdGVyU2NhbGFyQXJpdGhtZXRpYwpXZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL2NsaWNraG91c2Vwcm9maWxlZXhwb3J0ZXIvY2gucmVhZFRyZWVGcm9tTWFwChduZXQuKCpUQ1BDb25uKS5yZWFkRnJvbQocY3J5cHRvL3g1MDkuUGFyc2VDZXJ0aWZpY2F0ZQo0Z2l0aHViLmNvbS9wcm9tZXRoZXVzL2NvbW1vbi92ZXJzaW9uLmNvbXB1dGVSZXZpc2lvbgolcnVudGltZS9wcHJvZi4oKnByb2ZpbGVCdWlsZGVyKS5idWlsZAp7Z2l0aHViLmNvbS9vcGVuLXRlbGVtZXRyeS9vcGVudGVsZW1ldHJ5LWNvbGxlY3Rvci1jb250cmliL3JlY2VpdmVyL3Byb21ldGhldXNyZWNlaXZlci4oKnBSZWNlaXZlcikuaW5pdFByb21ldGhldXNDb21wb25lbnRzCh9ydW50aW1lL3Bwcm9mLigqcHJvZk1hcCkubG9va3VwCkBnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqVHVwbGUpLnBhcnNlCjZnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9zZXJ2aWNlLigqU2VydmljZSkuU3RhcnQKTGdpdGh1Yi5jb20vYXBhY2hlL2Fycm93L2dvL3YxNS9hcnJvdy9jb21wdXRlL2ludGVybmFsL2tlcm5lbHMuQ29tcGFyZUtlcm5lbHMKTmdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2V4cG9ydGVyL2V4cG9ydGVyaGVscGVyLigqbWV0cmljc1JlcXVlc3QpLkV4cG9ydAo8Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpiYXRjaCkuQXBwZW5kU3RydWN0CjZnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvLigqU2Vzc2lvbikuU3RhcnQuZnVuYzIKN2dvLm9wZW50ZWxlbWV0cnkuaW8vb3RlbC9hdHRyaWJ1dGUuY29tcHV0ZURpc3RpbmN0Rml4ZWQKQGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpBcnJheSkucGFyc2UKRWdpdGh1Yi5jb20vYWxlY3Rob21hcy9wYXJ0aWNpcGxlL3YyLigqZ2VuZXJhdG9yQ29udGV4dCkucGFyc2VNb2RpZmllcgo4Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NoLWdvL3Byb3RvLigqQ29sU3RyKS5EZWNvZGVDb2x1bW4KKGdpdGh1Yi5jb20vbWl0Y2hlbGxoL3JlZmxlY3R3YWxrLndhbGtNYXAKOWdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY29ubmVjdCkucHJvY2VzcwoRcnVudGltZS5pdGFic2luaXQKM2dpdGh1Yi5jb20vYXBhY2hlL2Fycm93L2dvL3YxNS9hcnJvdy9jb21wdXRlLmluaXQuMAqbAWdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2V4cG9ydGVyL2ludGVybmFsL3F1ZXVlLigqYm91bmRlZE1lbW9yeVF1ZXVlW2dvLnNoYXBlLmludGVyZmFjZSB7IEV4cG9ydChjb250ZXh0LkNvbnRleHQpIGVycm9yOyBJdGVtc0NvdW50KCkgaW50IH1dKS5Db25zdW1lCkNnaXRodWIuY29tL3Byb21ldGhldXMvY2xpZW50X2dvbGFuZy9wcm9tZXRoZXVzLigqc3VtbWFyeSkubmV3U3RyZWFtCiVnaXRodWIuY29tL21pdGNoZWxsaC9yZWZsZWN0d2Fsay53YWxrCkVnaXRodWIuY29tL2FsZWN0aG9tYXMvcGFydGljaXBsZS92Mi4oKmdlbmVyYXRvckNvbnRleHQpLnBhcnNlU2VxdWVuY2UKCG1haW4ucnVuCjZnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9vdGVsY29sLk5ld0NvbW1hbmQuZnVuYzEKigFnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcHJvY2Vzc29yL3Jlc291cmNlZGV0ZWN0aW9ucHJvY2Vzc29yL2ludGVybmFsLigqUmVzb3VyY2VQcm92aWRlcikuZGV0ZWN0UmVzb3VyY2UKGHJlZmxlY3QuKCpNYXBJdGVyKS5WYWx1ZQoqcnVudGltZS9wcHJvZi4oKnByb2ZpbGVCdWlsZGVyKS5hZGRDUFVEYXRhChpyZWdleHAuKCpSZWdleHApLmJhY2t0cmFjawohbmV0L2h0dHAuKCpwZXJzaXN0Q29ubikud3JpdGVMb29wChpnb2xhbmcub3JnL3gvbmV0L2h0bWwuaW5pdApeZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyLigqcHlyb3Njb3BlUmVjZWl2ZXIpLnJlYWRQcm9maWxlcwpeZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyLigqcHlyb3Njb3BlUmVjZWl2ZXIpLmhhbmRsZS5mdW5jMQpCZ29vZ2xlLmdvbGFuZy5vcmcvcHJvdG9idWYvaW50ZXJuYWwvZmlsZWRlc2MuKCpFbnVtKS51bm1hcnNoYWxGdWxsCjdrOHMuaW8vYXBpbWFjaGluZXJ5L3BrZy9ydW50aW1lLigqU2NoZW1lKS5BZGRLbm93blR5cGVzCiVuZXQvaHR0cC4oKnRyYW5zZmVyV3JpdGVyKS5kb0JvZHlDb3B5CjBnaXRodWIuY29tL2hldHpuZXJjbG91ZC9oY2xvdWQtZ28vdjIvaGNsb3VkLmluaXQKGm5ldC9odHRwLigqVHJhbnNwb3J0KS5kaWFsChNuZXQvaHR0cC5nZXRDb3B5QnVmCklnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci4oKnJldHJ5U2VuZGVyKS5zZW5kCitnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLmluaXQuZnVuYzM2CjtnaXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9mbGF0ZS4oKmNvbXByZXNzb3IpLnN0b3JlRmFzdApSZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby9nb2RlbHRhcHJvZi9pbnRlcm5hbC9wcHJvZi4oKnByb2ZpbGVCdWlsZGVyKS5mbHVzaAosZ2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS5lbmNvZGVTdHJpbmcKZGdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIuKCptZXRyaWNzRXhwb3J0ZXIpLmV4cG9ydEhpc3RvZ3JhbURhdGFQb2ludHMKFWVuY29kaW5nL2pzb24uTWFyc2hhbAozZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvY29uZm1hcC4oKkNvbmYpLk1lcmdlCkJnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9vdGVsY29sLigqY29uZmlnUHJvdmlkZXIpLkdldENvbmZtYXAKSWdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpBcnJheSkuYXBwZW5kUm93UGxhaW4KQ2dpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vZ29kZWx0YXByb2YuKCpIZWFwUHJvZmlsZXIpLlByb2ZpbGUKD3JlZmxlY3QuY29weVZhbAovZ2l0aHViLmNvbS92bXdhcmUvZ292bW9taS92aW0yNS90eXBlcy5pbml0LjMzMjkKQWdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpBcnJheSkuRW5jb2RlCllnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL2dvZGVsdGFwcm9mL2ludGVybmFsL3Bwcm9mLigqcHJvZmlsZUJ1aWxkZXIpLmVtaXRMb2NhdGlvbgo8Z2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby4oKmNwdVByb2ZpbGVDb2xsZWN0b3IpLlN0YXJ0CilrOHMuaW8vY2xpZW50LWdvL2t1YmVybmV0ZXMvc2NoZW1lLmluaXQuMAorcnVudGltZS9wcHJvZi4oKnByb2ZpbGVCdWlsZGVyKS5zdHJpbmdJbmRleAovZ2l0aHViLmNvbS92bXdhcmUvZ292bW9taS92aW0yNS90eXBlcy5pbml0LjU1NDQKK2dpdGh1Yi5jb20vZ29vZ2xlL3Bwcm9mL3Byb2ZpbGUuaW5pdC5mdW5jMzMKD2J5dGVzLmdyb3dTbGljZQoZbmV0L2h0dHAuKCpSZXF1ZXN0KS53cml0ZQpgZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci4oKm1ldHJpY3NFeHBvcnRlcikuZXhwb3J0TnVtYmVyRGF0YVBvaW50CkFnb29nbGUuZ29sYW5nLm9yZy9wcm90b2J1Zi9pbnRlcm5hbC9maWxlZGVzYy4oKkZpbGUpLmxhenlJbml0T25jZQo3Z2l0aHViLmNvbS9rbGF1c3Bvc3QvY29tcHJlc3MvZmxhdGUuKCpmYXN0RW5jTDEpLkVuY29kZQonZ2l0aHViLmNvbS9taXRjaGVsbGgvY29weXN0cnVjdHVyZS5Db3B5Ci9naXRodWIuY29tL2FsZWN0aG9tYXMvcGFydGljaXBsZS92Mi52aXNpdC5mdW5jMQpMZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyLnBvc3RQcm9jZXNzUHJvZgo6Z2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvc2NyYXBlLm11dGF0ZVNhbXBsZUxhYmVscwpGZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3Ivc2VydmljZS90ZWxlbWV0cnkubmV3U2FtcGxlZExvZ2dlci5mdW5jMQouZ2l0aHViLmNvbS9taXRjaGVsbGgvY29weXN0cnVjdHVyZS5Db25maWcuQ29weQoWcmVnZXhwLigqUmVnZXhwKS5TcGxpdAo+Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjb25uZWN0KS5wcmVwYXJlQmF0Y2gKFnJlZmxlY3QudmFsdWVJbnRlcmZhY2UKYmdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIuKCptZXRyaWNzRXhwb3J0ZXIpLmV4cG9ydFN1bW1hcnlEYXRhUG9pbnRzCipnaXRodWIuY29tL2Jlb3JuNy9wZXJrcy9xdWFudGlsZS5uZXdTdHJlYW0KMWdpdGh1Yi5jb20vc2hpcm91L2dvcHN1dGlsL3YzL2NwdS5JbmZvV2l0aENvbnRleHQKE3JlZ2V4cC9zeW50YXgucGFyc2UKeGdpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9yZWNlaXZlci9wcm9tZXRoZXVzcmVjZWl2ZXIvaW50ZXJuYWwuKCp0cmFuc2FjdGlvbikuZ2V0TWV0cmljcwonZ2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS5tYXJzaGFsCktnaXRodWIuY29tL2FwYWNoZS9hcnJvdy9nby92MTUvYXJyb3cvY29tcHV0ZS4oKlNjYWxhckZ1bmN0aW9uKS5BZGROZXdLZXJuZWwKKWdpdGh1Yi5jb20vZ29vZ2xlL3Bwcm9mL3Byb2ZpbGUudW5tYXJzaGFsChNidWZpby5OZXdSZWFkZXJTaXplCkZnb29nbGUuZ29sYW5nLm9yZy9wcm90b2J1Zi9yZWZsZWN0L3Byb3RvcmVnaXN0cnkuKCpGaWxlcykuUmVnaXN0ZXJGaWxlCkZnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9tb2RlbC9sYWJlbHMuKCpTY3JhdGNoQnVpbGRlcikuTGFiZWxzCjZnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy91dGlsL3Bvb2wuKCpQb29sKS5HZXQKXGdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIuKCptZXRyaWNzRXhwb3J0ZXIpLmNvbGxlY3RGcm9tTWV0cmljCl5naXRodWIuY29tL2FwYWNoZS9hcnJvdy9nby92MTUvYXJyb3cvY29tcHV0ZS9pbnRlcm5hbC9rZXJuZWxzLmdlbkNvbXBhcmVLZXJuZWxbZ28uc2hhcGUuaW50NjRdCilnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLlBhcnNlRGF0YQokZ2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS5pbml0ChBzeW5jLigqUG9vbCkuR2V0CkRnaXRodWIuY29tL2FsZWN0aG9tYXMvcGFydGljaXBsZS92Mi4oKmdlbmVyYXRvckNvbnRleHQpLnBhcnNlQ2FwdHVyZQoUbmV0L2h0dHAuaW5pdC5mdW5jMTUKQGdvb2dsZS5nb2xhbmcub3JnL3Byb3RvYnVmL2ludGVybmFsL2ZpbGVkZXNjLigqRmlsZSkubGF6eVJhd0luaXQKNmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jaC1nby9wcm90by4oKkNvbEZsb2F0NjQpLkFwcGVuZAowZ2l0aHViLmNvbS9nb2NjeS9nby1qc29uL2ludGVybmFsL2RlY29kZXIuaW5pdC4wCkxnaXRodWIuY29tL3Byb21ldGhldXMvY2xpZW50X2dvbGFuZy9wcm9tZXRoZXVzLigqU3VtbWFyeVZlYykuV2l0aExhYmVsVmFsdWVzCldnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9pbnRlcm5hbC9mYW5vdXRjb25zdW1lci4oKm1ldHJpY3NDb25zdW1lcikuQ29uc3VtZU1ldHJpY3MKK2dpdGh1Yi5jb20vcHJvbWV0aGV1cy9jb21tb24vdmVyc2lvbi5pbml0LjAKN2dpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqc3RydWN0TWFwKS5NYXAKKWdpdGh1Yi5jb20vYWxlY3Rob21hcy9wYXJ0aWNpcGxlL3YyLnZpc2l0CjxnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmNsaWNraG91c2UpLmFjcXVpcmUKGnN5bmMuKCpwb29sQ2hhaW4pLnB1c2hIZWFkCipnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLmluaXQuZnVuYzMKJmdpdGh1Yi5jb20vc2hpcm91L2dvcHN1dGlsL3YzL2NwdS5JbmZvCjhnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvLigqU2Vzc2lvbikudGFrZVNuYXBzaG90cwojbWltZS9tdWx0aXBhcnQuKCpXcml0ZXIpLkNyZWF0ZVBhcnQKRGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpBcnJheSkuQXBwZW5kUm93CiFjb21wcmVzcy9mbGF0ZS4oKmNvbXByZXNzb3IpLmluaXQKNmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqYmF0Y2gpLkFwcGVuZAo5Z28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvY29uZm1hcC4oKlJlc29sdmVyKS5SZXNvbHZlCg5zdHJpbmdzLlNwbGl0Tgo3Z28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvY29tcG9uZW50LlN0YXJ0RnVuYy5TdGFydAo6Z2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby4oKlNlc3Npb24pLmR1bXBIZWFwUHJvZmlsZQoVcnVudGltZS5uZXdwcm9jLmZ1bmMxCiJjb21wcmVzcy9mbGF0ZS4oKmNvbXByZXNzb3IpLmNsb3NlClpnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvY2xpY2tob3VzZXByb2ZpbGVleHBvcnRlci9jaC4oKkxpbWl0ZWRQb29sKS5wdXQKGXJlZmxlY3QuVmFsdWUuU2V0TWFwSW5kZXgKH2VuY29kaW5nL2pzb24ubWFwRW5jb2Rlci5lbmNvZGUKPWdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS5uZXdTY3JhcGVQb29sLmZ1bmMxLjEKVWdpdGh1Yi5jb20vcHJvbWV0aGV1cy9jbGllbnRfZ29sYW5nL3Byb21ldGhldXMuKCpTdW1tYXJ5VmVjKS5HZXRNZXRyaWNXaXRoTGFiZWxWYWx1ZXMKOmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLkRlZmF1bHREaWFsU3RyYXRlZ3kKRWdpdGh1Yi5jb20vcHJvbWV0aGV1cy9jbGllbnRfZ29sYW5nL3Byb21ldGhldXMudjIuTmV3U3VtbWFyeVZlYy5mdW5jMQoTcnVudGltZS5zeXN0ZW1zdGFjawpaZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci4oKm1ldHJpY3NFeHBvcnRlcikucHVzaE1ldHJpY3NEYXRhCl1naXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLigqbWV0cmljc0V4cG9ydGVyKS5jb2xsZWN0RnJvbU1ldHJpY3MKLWdvb2dsZS5nb2xhbmcub3JnL3Byb3RvYnVmL3Byb3RvLnByb3RvTWV0aG9kcwodY29tcHJlc3MvZmxhdGUubmV3RGVmbGF0ZUZhc3QKJ2dpdGh1Yi5jb20vc25vd2ZsYWtlZGIvZ29zbm93Zmxha2UuaW5pdApDZ2l0aHViLmNvbS9wcm9tZXRoZXVzL2NsaWVudF9nb2xhbmcvcHJvbWV0aGV1cy4oKlJlZ2lzdHJ5KS5SZWdpc3RlcgpnZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyLm5ld1B5cm9zY29wZVJlY2VpdmVyLk5ld0RlY29tcHJlc3Nvci5mdW5jMgpZZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby9nb2RlbHRhcHJvZi9pbnRlcm5hbC9wcHJvZi4oKnByb2ZpbGVCdWlsZGVyKS5Mb2NzRm9yU3RhY2sKDnJ1bnRpbWUucnQwX2dvCjpnb29nbGUuZ29sYW5nLm9yZy9wcm90b2J1Zi9pbnRlcm5hbC9maWxlZGVzYy5CdWlsZGVyLkJ1aWxkCiVnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLlBhcnNlChdieXRlcy4oKkJ1ZmZlcikuV3JpdGVUbwpGZ2l0aHViLmNvbS9hcGFjaGUvYXJyb3cvZ28vdjE1L2Fycm93L2NvbXB1dGUuR2V0RnVuY3Rpb25SZWdpc3RyeS5mdW5jMQo/Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjb25uZWN0KS5wcm9maWxlRXZlbnRzCj1naXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLlR5cGUuQ29sdW1uCkJnaXRodWIuY29tL2FsZWN0aG9tYXMvcGFydGljaXBsZS92Mi4oKmdlbmVyYXRvckNvbnRleHQpLnBhcnNlR3JvdXAKT2dpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vaW50ZXJuYWwvcHByb2YuZGVmYXVsdENvbGxlY3Rvci5TdGFydENQVVByb2ZpbGUKNmdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28uKCpTZXNzaW9uKS5TdGFydC5mdW5jMQo4Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjb25uZWN0KS5oYW5kbGUKLWdpdGh1Yi5jb20vZ29vZ2xlL3Bwcm9mL3Byb2ZpbGUuZW5jb2RlTWVzc2FnZQoiZ29sYW5nLm9yZy94L25ldC90cmFjZS5OZXdFdmVudExvZwpoZ2l0aHViLmNvbS9vcGVuLXRlbGVtZXRyeS9vcGVudGVsZW1ldHJ5LWNvbGxlY3Rvci1jb250cmliL3JlY2VpdmVyL3Byb21ldGhldXNyZWNlaXZlci4oKnBSZWNlaXZlcikuU3RhcnQKF3JlZmxlY3QuVmFsdWUuSW50ZXJmYWNlCkRnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqVHVwbGUpLkFwcGVuZFJvdwp/Z2l0aHViLmNvbS9vcGVuLXRlbGVtZXRyeS9vcGVudGVsZW1ldHJ5LWNvbGxlY3Rvci1jb250cmliL3JlY2VpdmVyL3Byb21ldGhldXNyZWNlaXZlci9pbnRlcm5hbC4oKm1ldHJpY0dyb3VwKS50b051bWJlckRhdGFQb2ludAodY29tcHJlc3MvZ3ppcC4oKlJlYWRlcikuUmVzZXQKR2dpdGh1Yi5jb20vcHJvbWV0aGV1cy9jbGllbnRfZ29sYW5nL3Byb21ldGhldXMuKCpSZWdpc3RyeSkuTXVzdFJlZ2lzdGVyChdydW50aW1lL3Bwcm9mLmFsbEZyYW1lcwqWAWdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2V4cG9ydGVyL2ludGVybmFsL3F1ZXVlLigqQ29uc3VtZXJzW2dvLnNoYXBlLmludGVyZmFjZSB7IEV4cG9ydChjb250ZXh0LkNvbnRleHQpIGVycm9yOyBJdGVtc0NvdW50KCkgaW50IH1dKS5TdGFydC5mdW5jMQoTbWFpbi5ydW5JbnRlcmFjdGl2ZQolZ28udWJlci5vcmcvemFwLigqTG9nZ2VyKS5XaXRoT3B0aW9ucwpBZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL2NvbHVtbi4oKlR1cGxlKS5FbmNvZGUKImNvbXByZXNzL2ZsYXRlLigqZGljdERlY29kZXIpLmluaXQKDnJ1bnRpbWUuZG9Jbml0CkBnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9zZXJ2aWNlL3RlbGVtZXRyeS5uZXdTYW1wbGVkTG9nZ2VyCixnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLmVuY29kZVZhcmludAoqZ2l0aHViLmNvbS9zcGYxMy9jb2JyYS4oKkNvbW1hbmQpLkV4ZWN1dGVDCjxnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmJhdGNoQ29sdW1uKS5BcHBlbmQKQ2dvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3BkYXRhL3BtZXRyaWMuTWV0cmljU2xpY2UuQXBwZW5kRW1wdHkKJGVuY29kaW5nL2pzb24uKCplbmNvZGVTdGF0ZSkubWFyc2hhbAoQc3RyaW5ncy5nZW5TcGxpdAolY29tcHJlc3MvZmxhdGUuKCpjb21wcmVzc29yKS5lbmNTcGVlZApPZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvcHJvY2Vzc29yL3Byb2Nlc3NvcmhlbHBlci4oKk9ic1JlcG9ydCkucmVjb3JkRGF0YQpLZ2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvc2NyYXBlLigqc2NyYXBlTG9vcCkuc2NyYXBlQW5kUmVwb3J0LmZ1bmMxCjtnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9vdGVsY29sLigqY29uZmlnUHJvdmlkZXIpLkdldApUZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvcHJvY2Vzc29yL3Byb2Nlc3NvcmhlbHBlci4oKk9ic1JlcG9ydCkuTWV0cmljc0FjY2VwdGVkCkZnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9zZXJ2aWNlL2ludGVybmFsL2dyYXBoLigqR3JhcGgpLlN0YXJ0QWxsCjNnaXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9mbGF0ZS4oKldyaXRlcikuQ2xvc2UKB2lvLkNvcHkKGnJlZ2V4cC5tZXJnZVJ1bmVTZXRzLmZ1bmMyChpyZWZsZWN0Lm1hcGFzc2lnbl9mYXN0c3RyMApLZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL2NvbHVtbi4oKkFycmF5KS5hcHBlbmRSb3dEZWZhdWx0ClxnaXRodWIuY29tL3Byb21ldGhldXMvY2xpZW50X2dvbGFuZy9wcm9tZXRoZXVzLigqbWV0cmljTWFwKS5nZXRPckNyZWF0ZU1ldHJpY1dpdGhMYWJlbFZhbHVlcwpBZ2l0aHViLmNvbS9hbGVjdGhvbWFzL3BhcnRpY2lwbGUvdjIuKCpnZW5lcmF0b3JDb250ZXh0KS5wYXJzZVR5cGUKMmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jaC1nby9wcm90by4oKkNvbFN0cikuQXBwZW5kChJyZWdleHAub25lUGFzc0NvcHkKOmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY29ubmVjdCkuc2VuZERhdGEKPms4cy5pby9hcGltYWNoaW5lcnkvcGtnL3J1bnRpbWUuKCpTY2hlbWUpLkFkZEtub3duVHlwZVdpdGhOYW1lChBydW50aW1lLm5ld3Byb2MxCjdnaXRodWIuY29tL3Byb21ldGhldXMvY29tbW9uL21vZGVsLkxhYmVsU2V0LkZpbmdlcnByaW50CnRnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcmVjZWl2ZXIvcHJvbWV0aGV1c3JlY2VpdmVyL2ludGVybmFsLigqdHJhbnNhY3Rpb24pLkNvbW1pdAoOcmVnZXhwLkNvbXBpbGUKMGdpdGh1Yi5jb20vZ29jY3kvZ28tanNvbi9pbnRlcm5hbC9lbmNvZGVyLmluaXQuMApIZ29vZ2xlLmdvbGFuZy5vcmcvcHJvdG9idWYvaW50ZXJuYWwvaW1wbC4oKk1lc3NhZ2VJbmZvKS5tYWtlUmVmbGVjdEZ1bmNzCjdnaXRodWIuY29tL0NsaWNrSG91c2UvY2gtZ28vcHJvdG8uQ29sSW50NjQuRW5jb2RlQ29sdW1uChJzdHJpbmdzLkZpZWxkc0Z1bmMKOmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY29ubmVjdCkucmVhZERhdGEKMnJ1bnRpbWUvcHByb2YuKCpwcm9maWxlQnVpbGRlcikuYXBwZW5kTG9jc0ZvclN0YWNrCjlnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9jb25mbWFwLigqUmV0cmlldmVkKS5Bc0NvbmYKP2dpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY2xpY2tob3VzZSkuZGlhbC5mdW5jMQozZ29vZ2xlLmdvbGFuZy5vcmcvcHJvdG9idWYvcmVmbGVjdC9wcm90b2Rlc2MuaW5pdC4wEggKBgC12SQAABJtCmsAcADyAQCyLwAtAFAAgwEA8+ICACgA76IOAOwCAN6IBQDxAgC9BADhAgC1kgYAmgEAgIABAF0Ak54DAPUBAJoDAOsBAICAAgB6AKtVAHwAgIABAJYCAJoDAK8CAAAA2AIADQDfAQCSCQDPAhJ4CnYAcACRAgBAAPABAPIuANcBAFAAlAEA8+ICAOwBAO+iDgDoAQDeiAUAdQC9BAC9AgCKkgYAigEAKwC0AgCAgAEA6wEAk54DAPQBAJoDABYAgIACALQCAKtVAFkAgIABALgCAJoDAIUDAAAAIAANAIsCAJIJAMYCEq0CCqoCAHAAQwBAQNkBAOQBAMUBABQIKwD6LACTAwBQAPoBAPPiAgDtAgDvog4ALgDVKgAcAJyOApyOAsEBABAAaADpBQBKAGIAjAEAyQQAlgMAtKYBAOcBAAAAjgMAAgKIAgCAAQC+AQAHAIwCAFUA1AIACwD5AQCcDpwOAgABAW8AmAEAVgADA44CAIBAALYCAGJiGAAAALMCAMkkACMAAACvAQDJJACtAgADAGoAAADzAQD2AgALAAcAWAC9BAA4AI+HBABjANWKAgD7AgAmAKkCACsAzAIAgIABABYAq3WrdZcCAEVFvAEAEADKAQDKmwIAwgEAyQzJDKkBAJoDABsAgIACAMwCAKtVAB8AgIABALEBAJoDAF0AAADmAQAAAJMBAA0APgCSCQCKAxKKAgqHAgBAAPgBADAA+wFA5AEAxwIACAjAAgAEBNMCCNUq1SrrAgClAqUCjQIAUAANAPPiAgBaAO+iDgCyAQDVKgDqApyOAhAAMQDpBQCNAwBiAI0DAMkEADsAtKYBAEwCgAEAjQMABwCfAQBVAI0DAAsAjQOdDpgBAI0DA4BAANYBYskkAI0DAMkkAI0DAAMAyAEAAAB9APYCAjYABwCNAwC9BAAHANaqAQDLAgC5nAIAUQCAQIBAqAIA1YoCAIwDACYmcgArACUAgIABABvwdRAA1gIA8gMATwDYlwIA2gLJDJoDAK8CAICAAgAlAKtVAGQAgIABAJYCAJoDAOsBAA0A4AIAkgmSCasBEocCCoQCAEAAJAAwAK4CQOQBAPkCji1QAAUA8+ICAPQCAImdCAClAQDmhQYAzwEA1SrVKtUCnI4CEADpAgDpBQB0AGIAdADJBADSAgC0pgEASwKAAQB0AAcAnwEAVQB0AAsAdJ0OmAEAugEDgEAAEGLJJAC6AQDJJAC6AQADANkCAPQCANUBAgcAdAC9BADFAgDVKgCYAgCBgAGBgAGLAQC5nAIAogGAQNXKAQCiAgCAQAC1AiYrAIQDAICAAQCvAvB1EAAxABMASQDfAwCjAgCrlQIArAIArQKtArAByQyaAwCFAwCAgAKAgAIzAKsVAIADAIBAAL4CAICAAQC4AgCaAwAWAA0NqgES/QEK+gEAQACeAQAwMLACQOQBALsBji1QUOQCAPPiAgCmAQCJnQgA/AEA5oUGAPwB8bgCEABbAOkFAMMBAGIAmAEAyQQAXgC0pgEA3AICgAEAmAEABwC/AQBVAMMBAAsAmAGdDpgBAKECA4BAgEBGYskkySRzAMkkAKECAAMApwIA9AL0AswBAgcAmAEAvQQLhgIA1SrVKhGBgAGAgAKAgAI3ALkcuRzEAYBAgECAQPYCANWKAQBrAIBAAG0mKwDOAgCAgAEAhQPwdRAA6QIAEwC5AQDfAwC3AQCrlQIApQL2DpoDAF2AgAKrFQCeAQCAQAATAICAAQCxAQCaAwAbEtUBCtIBAEAAggFw5AEAnAHeLfPiAgDtAQCJnQgAyQEA5oUGAMkB8bgCEBDwAgDpBQA8AGIAaQDJBACJAQCacwC2AQCaMwDSAQKAAYABQgAHAPcBAFUAPAALC4cDnQ6YAQAXrmXJJMkkcwADAI8B9gIHB4cDALIEAMsBmscE1YoBAOgCAIBAAG0mKwDpAQCAgAEAXfB1EABbABMTkAIAqwEA4wIAEhLzAgCiAqICgAIAq5UCALUB9g6aAwDrAYCAAqsVANsCAIBAAEkAgIABAJYCAJoDAK8CEsUBCsIBAEAAgANw5AHkAZcB3i3z4gIArAEAiZ0IAN0BAOaFBgADgbkC6QUAPABiAAoAyQQAjwMAmnMA3AEAmjOaM6QCggEHB4kDAFUAPKgOmAEAZveJAQMDKf0CpgQA1wIAAgCFAQAKAHmaxwTVigHVigFlAIBAACYmKyufAgCAgAEA6wHwdRAQ8AITqwEAmQG0AqtVq1WPAgCAgAEAVQCAQIBA/QH2DpoDABaAgAKrFQBJAIBAgEC5AQCAgAEAuAIAmgMAhQMSlQEKkgEAQACeAbIw8+ICAIgBAImdCADQAgDmhQYAkgGBuQLpBQA8AGJigQMAyQQAQQCaMwASAIBAAE2jNFVVHqgOmAEAU/eMAYEDgQMVAKUBjQGKAgACAP8BAAoA/wLv0QWAQAD9AlGAgAEAFpN2qwEA4wLfV4CAAQC1AfZOmgMAG4CAAqsVALkBgECAgAEAsQEAmgMAXRKaAQqXAQBAAFyyMKvBAgD8AgABAEgAjwMA2wEAuB4AhAIA9PEBACEAlasGANECAOaFBgBngbkC6QUAPGLJBAAvAJozmjMyAIBAABKgQ5gBmAEw+I8BGAD/AY0BAgJfAAoAKu/RBYBAAPoCUYCAAQAbk3arAQAM31eAgAEAuwL2TpoDAN8CgIACqxWrFZACgECAgAEAlgIAmgMA6wEStwEKtAEAQADUAbIwq8ECAMICAAEAfwCPAwD+AgBVAIMCAOMdAMICADQAewCbZADHAQCljQEA3gEAlasGAKoCAKusAQD1AgC/1gEAewCW1gGW1gHTAQCMqwHVqgHIAgDaAQDHAYG5AukFADxiyQQAkwKaM4BAANEBsNQBGAxfjwEKAP4B79EFgEAAhwFRgIABAN8Ck3arAasB8wLfV4CAAYCAAQb2TpoDAM4Bq9UCgIABALgCAJoDABYS4QEK3gEAQACAA7IwgMACABoAqwEAlAMAAQC4AQArAOUCAOQCAMQCAFUAgAEAqwEAjgEAnA4AowEAnA4AgwIAMACcAgAEALkCAJtkAEQApQ0AwQIAgIABgIABtwIAhHsAcADm2gIAgQIAq9UCAJ4CACgohgMA7qsBAMABABUVUgAEALkCALvWAQCcApbWATcAoAHVqgGrAQBEAC8AiAOBuQLpBQA8YskEALECmjOAQIBAqwKw1AEMDLQBmwEKAJQC79EFgEAAeFGAgAEAzgGTngOaAwBdq9UCgIABALEBAJoDABsS0AEKzQEAQECeAbIwgEAArgEAgIACAB0AqwEAGgABAPICACsA2AEA5AIAPQBVVcYBAKsBABoAnA4AlQIAnA4AUAAwAJsCAAQAmwEAm2QA5QEApQ0AGYCAAYR7NpICAJGwAgBsANUqAIsDAKvVAgAnKO6rAQC/AhUEAJsBAJABAJsCAKvVAQDNAZbWATc3ugLVqgGrAQDlAQAvAAmBuQLpBQA8YskEAKQB8cgCCgo679EFgEAAIlGAgAEAXZOeA5oDAOsBq9UCgIABAJYCAJoDAN8CEuIBCt8B8jCAQIBAxgEAgIACAJUCAKsBAK4BAAEA7gIAKwCNAQDkAgBMVasBAK4BAJwOAJoCAJwOAJUCADAAcQAEAM0CAJtkAOICAIAIgAiyAgClBaUFhgOAgAGWXslEggIAuBwAlgE22ZMCAIICALgcAJYBANUq1Sp+AICAAgCCAgCrVatVlgEo7qsBAIUCFQQAzQIAkAEAcQCr1QEAkgOigQOrAQDiAgANAEAAIgCJAoG5AukFAIYBYskEAFfqmgiAQIBA4AFRgIABAOsBk54DmgMAFqvVAoCAAQC4AgCaAwDOARK6AQq3AfJwgIACAJoCAKsBAEUAAQDQAQArAGEA5AIAS1WrAQBFAJwOAJ0BAJwOAJoCADAAswEABACVAwCbZKor3QKljQHNGQD3AslEuBwAdzbZkwIA9wIAuBwAd9UqgIACAPcC01XuqwEAgwMVBACVAwCQAQCzAQCr1QEAvQGigQOrAasB3QIADQ0/ACIA7wKBuQLpBekFHmLJBMkE9gG72wiAgAEAFpOeA5oDABur1QKAgAEAsQEAmgMAXRKqAQqnAfJwgIACAJ0BAKsBAKcBAAEADwArADQA5AIAOVWrAQCnAQCcDgDqAQCcDgCdAQAwAAgABACBAQDxOACSA8+4Ac0ZAKgByUS4HLgcDjbZkwIAqAEAuBy4HA7VKoCAAgCoAdNV7qsBAIQBFQQAgQEAkAEACACr1QEA3gLaggMFAEAAEACJAgANDWDQnwuAgAEAG5OeA5oDAK8Cq9UCgIABAJYCAJoDAOsBEp4BCpsB8nCAgAIA6gEAqwEApwEAAQCZAgArACwA5AIA7gFVqwEApwEAnA4AFACcDgDqAQAwAPEBAAQERwDxOAC9Ac+4Ac0ZzRnKArdh2ZMC2RPKAo1HgIACAMoC01XuqwEAhAEVBASmAgCQAQDxAQCr1QEA4QHaggMFBT8AEADvAt2fC4CAAQCvApOeA5oDAIUDq9UCgIABALgCAJoDABYSjQEKigHycICAAgDkAQCrAQCnAQABAaEBACsA6QEA5AIAvAJVqwEApwEAnA4A6gEAnA4A5AEAMDCQAQTxOABu07MCgIACAK0B5lqAgAIA7wHTVe6rAQDnAhmQAZABkAEAq9UBAN4C34IDDABAAAQEkAPdnwuAgAEAhQOTngOaAwBdq9UCgIABALEBAJoDABsShgEKgwHycICAAgDqAQCrAasBpwEBKyufAgDkAgCgAlWrAasBpwEAnA4A5AEAnA4A6gE08TjxOOMB07MCgIACgIAChwLmWoCAAoCAAocC01VMTIYDAKKrAQC/AqkBq9UBANoB34IDDAw/4Z8LgIABAF2TngOaAwDrAavVAoCAAQCWAgCaAwCvAhJaCljycICAAgAU1wGAAQBUAOQB5AGRA4ACnA4A6gEAnA4A5AH9nQiiqwEAhQKpAYCAAQDDAgCrVatV3gLMog6AgAEA6wGTngOaAwAWq9UCgIABALgCAJoDAIUDElAKTvJwgIACgIAC6gHXAYABgAGmAuQDnA4AFACcDgDqAf2dCKKrAQCDA6kBgIABgIAB+AL39w6AgAEAFpOeA5oDABur1QKAgAEAsQEAmgMAXRI4Cjat9wKcDgDqAQCcDgDkAf2dCKKrAQCEAaD5D4CAAQAbk54DmgMArwKr1QKAgAEAlgIAmgMA6wESPQo7rfcCnA4A5AEAnA4AlQH9nQhNAIQBANWqAQDmAqD5D4CAAQCvApOeA5oDAIUDq9UCgIABALgCAJoDABYSPAo6rfcCnA4AlQEAnA4AyQL9nQhNAOcCANWqAQCdAqD5D4CAAQCFA5OeA5oDAF2r1QKAgAEAsQEAmgMAGxI8Cjqt9wKcDgDJAgCcDgAE/Z0ITU1iANWqAdWqAXag+Q+AgAEAXZOeA5oDAOsBq9UCgIABAJYCAJoDAN8CEi8KLa33ApwOAAQAnA6cDoIDv8IZgIABAOsBk54DmgMAFqvVAoCAAQC4AgCaAwDOARIoCiat9wKcDpwOggPb0BmAgAEAFpOeA5oDABur1QKAgAEAsQEAmgMAXRIhCh+k1hyAgAEAG5OeA5oDAN8Cq9UCgIABAJYCAJoDAOsBEiEKH6TWHICAAQDfApOeA5oDAM4Bq9UCgIABALgCAJoDABYSIQofpNYcgIABAM4Bk54DmgMAXavVAoCAAQCxAQCaAwDiARIgCh6k1hyAgAEAXZOeA5oDADWr1QKAgAEAlgIAmgMAkQESJgokpNYcgIABgIAB6wGTngOaA5oDTqvVAoCAAYCAAbgCAJoDmgNOEgAYtdkkIJyOAg=="; - let b_req = base64::decode(req).unwrap(); - let b_resp = base64::decode(resp).unwrap(); - merge_tree(0, b_req.as_slice(), "alloc_objects:count".to_string()); - let res = export_tree(0, "alloc_objects:count".to_string()); - let o_expected = SelectMergeStacktracesResponse::decode(b_resp.as_slice()).unwrap(); - let o_res = SelectMergeStacktracesResponse::decode(res.as_slice()).unwrap(); - let fg = &o_expected.flamegraph.unwrap(); - let res_fg = &o_res.flamegraph.unwrap(); - for i in 0..fg.levels.len() { - let mut expected_level: Vec<String> = Vec::new(); - let mut res_level: Vec<String> = Vec::new(); - for j in 0..fg.levels[i].values.len() / 4 { - expected_level.push( - fg.names[fg.levels[i].values[j * 4 + 3] as usize].clone() + ":" + - fg.levels[i].values[j * 4 + 1].clone().to_string().as_str() + ":" + - fg.levels[i].values[j * 4 + 2].clone().to_string().as_str() + ":" + - fg.levels[i].values[j * 4 + 0].clone().to_string().as_str() - ); - } - for j in 0..res_fg.levels[i].values.len() / 4 { - res_level.push( - res_fg.names[fg.levels[i].values[j * 4 + 3] as usize].clone() + ":" + - res_fg.levels[i].values[j * 4 + 1].clone().to_string().as_str() + ":" + - res_fg.levels[i].values[j * 4 + 2].clone().to_string().as_str() + ":" + - res_fg.levels[i].values[j * 4 + 0].clone().to_string().as_str() - ); - } - expected_level.sort(); - res_level.sort(); - expected_level.insert(0, "level {}:".to_string() + i.to_string().as_str()); - res_level.insert(0, "level {}:".to_string() + i.to_string().as_str()); - assert_eq!(expected_level, res_level); - } - } - - #[test] - fn test_diff() { - let l_b64_req = "8gJFYiBVOCYNuEtnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci4oKmxvZ3NSZXF1ZXN0KS5FeHBvcnTu5d7bL0QqHRtuZXQuKCpzeXNEaWFsZXIpLmRpYWxTaW5nbGXQtZgxgpXYNBFydW50aW1lLm5vdGVzbGVlcA/EJYLTmdXHNGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jaC1nby9wcm90by4oKlJlYWRlcikucmVhZEZ1bGz3ctP2qgjz6DxnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9zY3JhcGUuKCpzY3JhcGVMb29wKS5yZXBvcnTy394COxe+5RJydW50aW1lLmNvbnZUc2xpY2WeIu8KUN1k1DVnaXRodWIuY29tL213aXRrb3cvZ28tY29ubnRyYWNrLmRpYWxDbGllbnRDb25uVHJhY2tlcsOwYDTe+2s8a2dpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9yZWNlaXZlci9wcm9tZXRoZXVzcmVjZWl2ZXIvaW50ZXJuYWwuZ2V0U2VyaWVzUmVm/X+kiP3agLEyZ28ub3BlbnRlbGVtZXRyeS5pby9vdGVsL3Nkay90cmFjZS4oKnRyYWNlcikuU3RhcnQQR4RwgXBT0DVnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvLigqU2Vzc2lvbikudXBsb2FkRGF0YQYmn4SxwWKvH3J1bnRpbWUuKCpzdGtmcmFtZSkuZ2V0U3RhY2tNYXDis0kP+uiw3Q1jb250ZXh0LnZhbHVl4G5kL5JSOlaKAWdpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9yZWNlaXZlci9wcm9tZXRoZXVzcmVjZWl2ZXIvaW50ZXJuYWwuKCppbml0aWFsUG9pbnRBZGp1c3RlcikuYWRqdXN0TWV0cmljU3VtbWFyeSHZ+HhuiwONGnJ1bnRpbWUuKCptY2FjaGUpLm5leHRGcmVlM77u2s/L+2E4Z2l0aHViLmNvbS9td2l0a293L2dvLWNvbm50cmFjay5OZXdEaWFsQ29udGV4dEZ1bmMuZnVuYzEgc4I1iE+vYClydW50aW1lLigqaW5saW5lVW53aW5kZXIpLnJlc29sdmVJbnRlcm5hbMJAbvbcb1HqD3J1bnRpbWUubWVtbW92ZZuh/V+iOEhbVmdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIucmVtb3ZlUHJvbUZvcmJpZGRlblJ1bmVzLmZ1bmMxuvc1VEWHJhgsZ2l0aHViLmNvbS9rbGF1c3Bvc3QvY29tcHJlc3MvZmxhdGUubG9hZDMyMzLPVSBP+m3oYktnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9zY3JhcGUuKCpzY3JhcGVMb29wKS5zY3JhcGVBbmRSZXBvcnQuZnVuYzIzmGHnJbPurDBnaXRodWIuY29tL0NsaWNrSG91c2UvY2gtZ28vcHJvdG8uKCpSZWFkZXIpLlJlYWSlYkFtlSd0BA1ydW50aW1lLndha2VwlLf3+v5y09ENcnVudGltZS5tY2FsbPuNl6PuA6U5DnJ1bnRpbWUuc3Bhbk9mI9NTkMyWW7cPcnVudGltZS5lZmFjZWVxSzJunQ9Uhp0ZY29udGV4dC4oKmNhbmNlbEN0eCkuRG9uZfMsZULPVpCrGnJ1bnRpbWUuKCpyYW5kb21FbnVtKS5uZXh0kWNjtyeGAmlAZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL3Byb3RvLigqQmxvY2spLkFwcGVuZDnWw6fOTmtEEmZtdC4oKnBwKS5kb1ByaW50ZqbGC4nJ7PpEEXJ1bnRpbWUuZ3Jvd3NsaWNlGEQvj3jClhEsbmV0LigqUmVzb2x2ZXIpLmdvTG9va3VwSVBDTkFNRU9yZGVyLmZ1bmMzLjEV5+nLyvXLSBBydW50aW1lLmZpbmRudWxs5lst2nIIqXBRZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci5iYXRjaFNhbXBsZXNBbmRUaW1lU2VyaWVzZfFhRnq9GiwccnVudGltZS5tZW1jbHJOb0hlYXBQb2ludGVyc1W3xIUQn91DUWdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3Byb2Nlc3Nvci9wcm9jZXNzb3JoZWxwZXIuTmV3TWV0cmljc1Byb2Nlc3Nvci5mdW5jMWPoFr12xtVpN2dpdGh1Yi5jb20va2xhdXNwb3N0L2NvbXByZXNzL2ZsYXRlLigqY29tcHJlc3NvcikuY2xvc2U27TmsGdFln0pnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci5uZXdRdWV1ZVNlbmRlci5mdW5jMdUdZfSJPqEUF25ldC9odHRwLigqQ2xpZW50KS5zZW5kKiYD0avBXyMVbmV0L2h0dHAuKCpDbGllbnQpLkRvAZz1EQGpIwIYdW5pY29kZS91dGY4LlZhbGlkU3RyaW5nGF2inv8oC/gfbmV0L2h0dHAuKCp3YW50Q29ubikudHJ5RGVsaXZlcstXqaoQaVrtE3J1bnRpbWUuYWRqdXN0ZnJhbWVVjVMQssvOnhlydW50aW1lLm1Qcm9mX0ZsdXNoTG9ja2VkzeOe990RHUoSZm10LigqcHApLnByaW50QXJn80cdjE5muuoQcnVudGltZS5zZWxlY3Rnb0N4w9HQ5G8xEXJ1bnRpbWUuc3RrYnVja2V0cetsxvuvn0swZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby4oKlNlc3Npb24pLnJlc2V09/uG9ltwyP4pZ29sYW5nLm9yZy94L25ldC90cmFjZS4oKmV2ZW50TG9nKS5FcnJvcmazO1wJckN8mRRydW50aW1lLmVudGVyc3lzY2FsbMMD/mcNHsdfGHJ1bnRpbWUuKCpibWFwKS5vdmVyZmxvdz7FihbCxxnQFnJ1bnRpbWUuZnVuY0luZm8uZW50cnmMoFlkje2cCBFydW50aW1lLnN0ZWFsV29ya+hEKSTHoql0GXJ1bnRpbWUubWFwYXNzaWduX2Zhc3RzdHI5Ud4AVNJryBVydW50aW1lLmNhbGxlcnMuZnVuYzF4RA+910vDWDpnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmJhdGNoKS5jbG9zZVF1ZXJ50+XufhUB6MQ0Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NoLWdvL3Byb3RvLigqUmVhZGVyKS5SZWFkQnl0ZZUYf1XL2xlNGXJ1bnRpbWUuZ2NNYXJrVGVybWluYXRpb24aXNyQkNuGzR5ydW50aW1lLmZ1bmNOYW1lUGllY2VzRm9yUHJpbnQUeHFuv1RuaRFydW50aW1lLnBNYXNrLnNldCgpYBbs7bb8EnJ1bnRpbWUud2JCdWZGbHVzaGrPW2Nc3Uw9TmdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIuYnVpbGRQcm9tQ29tcGxpYW50TmFtZSKliK2s2XlyGWNvbnRleHQuV2l0aERlYWRsaW5lQ2F1c2W4yv5Aftm3nw5zeXNjYWxsLnNvY2tldGtE6nlU7pgAInJ1bnRpbWUvaW50ZXJuYWwvc3lzY2FsbC5FcG9sbFdhaXTuxwEiIm09KiFydW50aW1lL2ludGVybmFsL3N5c2NhbGwuRXBvbGxDdGzHlVgnObRT3BJyZWZsZWN0LnVuc2FmZV9OZXeaLOHNe9MYbB9pbnRlcm5hbC9wb2xsLnJ1bnRpbWVfcG9sbENsb3NlJY3DtA8lHN0ScnVudGltZS5hcmVuYUluZGV4REQbHue2svIOcnVudGltZS50Z2tpbGx+EV/AEe3NPjxnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLigqUHJvZmlsZSkuV3JpdGVVbmNvbXByZXNzZWRNbPflG/Lvw0NnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9zY3JhcGUuKCp0aW1lTGltaXRBcHBlbmRlcikuQXBwZW5k3UYgkVNtBmUWcnVudGltZS5nY1RyaWdnZXIudGVzdI0cOtJTIknQDnJ1bnRpbWUucGFya19t1+WHhhMaBcYUcnVudGltZS5maW5kUnVubmFibGWIhIddPW13hCJuZXQuKCpSZXNvbHZlcikubG9va3VwSVBBZGRyLmZ1bmMxqT4MLWUHADcPcnVudGltZS5jYWxsZXJz1DhuJdtcEFk8Z2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvc2NyYXBlLigqc2NyYXBlTG9vcCkuYXBwZW5kjxk8xF6hp9UQbmV0LigqY29ubikuUmVhZBD8kMwOtjQ7EnJ1bnRpbWUuZ3JleW9iamVjdAHrf6wTytYXdWdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9jbGlja2hvdXNlcHJvZmlsZWV4cG9ydGVyL2NoLigqY2xpY2tob3VzZUFjY2Vzc05hdGl2ZUNvbHVtbmFyKS5JbnNlcnRCYXRjaORurh8EWm6QE3J1bnRpbWUubVByb2ZfRmx1c2jElZrmeYJnOkJnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLmluaXQuZnVuYzJ+YAUA18Wl0mNnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLigqbWV0cmljc0V4cG9ydGVyKS5leHBvcnRIaXN0b2dyYW1EYXRhUG9pbnTFToumQIUHskhnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9jb25zdW1lci5Db25zdW1lTWV0cmljc0Z1bmMuQ29uc3VtZU1ldHJpY3NhAzhpySmARhBydW50aW1lLm5hbm90aW1l4CjGOG7ksrgYbmV0LigqUmVzb2x2ZXIpLmV4Y2hhbmdlQ8AfmTP86x8NaW5kZXhieXRlYm9keY3Yx8DIjQwiYWdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIuKCptZXRyaWNzRXhwb3J0ZXIpLmV4cG9ydE51bWJlckRhdGFQb2ludHOr1H67/MAsiBlieXRlcy4oKkJ1ZmZlcikuV3JpdGVCeXRlpOuvxc915m0RcnVudGltZS5zY2Fuc3RhY2uhDvLntYM3bh1ydW50aW1lLmdjRHJhaW5NYXJrV29ya2VySWRsZXJQTFSyRpE/Em5ldC5pbnRlcm5ldFNvY2tldBTKYqNsZmluEnJ1bnRpbWUuc2Nhbm9iamVjdOU7MKX9higGRmdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL21vZGVsL2xhYmVscy5MYWJlbHMuSGFzaFdpdGhvdXRMYWJlbHMmOKRJNa3t8TZnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLkVudW36+1wHs1y1EFJnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL2dvZGVsdGFwcm9mL2ludGVybmFsL3Bwcm9mLigqcHJvZmlsZUJ1aWxkZXIpLkJ1aWxkAZMCTbZVcZ5BZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjbGlja2hvdXNlKS5QcmVwYXJlQmF0Y2hzbPhDzBobL25naXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcmVjZWl2ZXIvcHJvbWV0aGV1c3JlY2VpdmVyL2ludGVybmFsLm5ld01ldHJpY0ZhbWlseXALxMr4X9nQDnJ1bnRpbWUuc3RhcnRt+aooldoDdxgNbmV0L2h0dHAuc2VuZASN4fD2fGK6IW5ldC9odHRwLigqVHJhbnNwb3J0KS5kaWFsQ29ubkZvcsrvkCcMcHfwH2dvLnViZXIub3JnL3phcC4oKkxvZ2dlcikuY2hlY2ta6sJ3kzMCPEFnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqQXJyYXkpLmFwcGVuZAwgkiQn4LowDmlvLlJlYWRBdExlYXN0gw5ZQNj+jTQTcnVudGltZS5wY2RhdGF2YWx1Zf1hQFweqGRURGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpJbnQ2NCkuQXBwZW5kUm93ar3ZNSS/hHwUcnVudGltZS5zcmNGdW5jLm5hbWWhWYKFcVt4yg9ydW50aW1lLnBjdmFsdWXK1qreFSyTUxRydW50aW1lLnByb2ZpbGVhbGxvY7ywal2vzkeKGGlvLigqTGltaXRlZFJlYWRlcikuUmVhZIVhlGNBEwfrNGdvLm9wZW50ZWxlbWV0cnkuaW8vb3RlbC9zZGsvdHJhY2UuKCp0cmFjZXIpLm5ld1NwYW7MhdTjfYCO2hhuZXQuKCpSZXNvbHZlcikubG9va3VwSVBjpqHpKMBUOhtuZXQuKCpzeXNEaWFsZXIpLmRpYWxTZXJpYWxX21iwY/69ahFydW50aW1lLm1ha2VzbGljZWzG8IPhmf1qRWdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS4oKnNjcmFwZUxvb3ApLnNjcmFwZUFuZFJlcG9ydI8Fh2IVyZG5OGdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL21vZGVsL2xhYmVscy5OZXdCdWlsZGVydWeYuFSqNqMWcnVudGltZS4oKmxmc3RhY2spLnBvcHH++jLUDnq1JWludGVybmFsL3NpbmdsZWZsaWdodC4oKkdyb3VwKS5kb0NhbGz317MU8G4OJhlydW50aW1lLigqZ2NXb3JrKS5iYWxhbmNlE4miI5YNKpFmZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL2NsaWNraG91c2Vwcm9maWxlZXhwb3J0ZXIuKCpjbGlja2hvdXNlUHJvZmlsZUV4cG9ydGVyKS5zZW5kYjECVAG5ft0ebmV0L2h0dHAuKCpUcmFuc3BvcnQpLmRpYWxDb25uGM3/cHirFUlfZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyL2NvbXByZXNzLigqRGVjb21wcmVzc29yKS5yZWFkQnl0ZXMobxUzr1RcERhydW50aW1lLmdyb3dXb3JrX2Zhc3RzdHLJerpVZipgQxBydW50aW1lLm5ld3N0YWNrLmC4JNoWycEecnVudGltZS4oKm1vZHVsZWRhdGEpLnRleHRBZGRy1dcTWLt4WMoxcnVudGltZS4oKmdjQ29udHJvbGxlclN0YXRlKS5maW5kUnVubmFibGVHQ1dvcmtlcoG//wcmlW+VOWdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS4oKnNjcmFwZUxvb3ApLnJ1brfIxI02Ehn8LmdvLm9wZW50ZWxlbWV0cnkuaW8vb3RlbC90cmFjZS5TcGFuRnJvbUNvbnRleHR7RRlDltQKokRnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL2dvZGVsdGFwcm9mL2ludGVybmFsL3Bwcm9mLmFsbEZyYW1lc64HJogkHSYRPGdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL2Rpc2NvdmVyeS4oKk1hbmFnZXIpLnNlbmRlcuvyaHNTRVcgFHJ1bnRpbWUudHJhY2ViYWNrUENzIewMtGorjmcWcnVudGltZS5yZWVudGVyc3lzY2FsbIpcj77pzHR6GXJ1bnRpbWUudHlwZVBvaW50ZXJzLm5leHRFKblWEEOtIBRuZXQuKCpPcEVycm9yKS5FcnJvchJ6ZtBJXy/RF3J1bnRpbWUuc2NhbmZyYW1ld29ya2Vyqiw1lUeqYEt0Z2l0aHViLmNvbS9vcGVuLXRlbGVtZXRyeS9vcGVudGVsZW1ldHJ5LWNvbGxlY3Rvci1jb250cmliL3JlY2VpdmVyL3Byb21ldGhldXNyZWNlaXZlci9pbnRlcm5hbC4oKnRyYW5zYWN0aW9uKS5BcHBlbmTznPwnLpKPDhBydW50aW1lLmdldGVtcHR5kPt1VpaW11cTcnVudGltZS5yZWFkVWludHB0coKOAoicndswFm5ldC5kbnNQYWNrZXRSb3VuZFRyaXCgZ6KBJdJevw9ydW50aW1lLmhhbmRvZmZk1WpYsh4gMVxnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci4oKm1ldHJpY3NTZW5kZXJXaXRoT2JzZXJ2YWJpbGl0eSkuc2VuZF2lQEZIiP3eKWVuY29kaW5nL2pzb24uKCplbmNvZGVTdGF0ZSkucmVmbGVjdFZhbHVlVq+T63lfgbUdZW5jb2RpbmcvYmluYXJ5LkFwcGVuZFV2YXJpbnTVIROTH7MTngltZW1lcWJvZHlNq1bN+mITNxZyZWZsZWN0LigqTWFwSXRlcikuS2V5VRtgDuujnTs1Z2l0aHViLmNvbS9wcm9tZXRoZXVzL2NvbW1vbi9tb2RlbC5MYWJlbFZhbHVlLklzVmFsaWRM/u9O9sACUh1ydW50aW1lLnN0YXJ0VGhlV29ybGRXaXRoU2VtYeQFfHBegZOLGnJ1bnRpbWUuZGVkdWN0QXNzaXN0Q3JlZGl0S1cekAHePHoOc3lzY2FsbC5Tb2NrZXRqIJ43AdmxyyhydW50aW1lLigqbXNwYW4pLnR5cGVQb2ludGVyc09mVW5jaGVja2VkRxLl4KV7CXIcY29tcHJlc3MvZ3ppcC4oKlJlYWRlcikuUmVhZAM3qLGFhYD7FnJ1bnRpbWUudW5sb2NrV2l0aFJhbmvYS1XuP9msnhJzeXNjYWxsLlJhd1N5c2NhbGwedrNbs+wNA09nby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci4oKmJhc2VSZXF1ZXN0U2VuZGVyKS5zZW5kryQJIXXRJvUfbmV0L2h0dHAuKCpUcmFuc3BvcnQpLnJvdW5kVHJpcKui8EyD3+S2KXJ1bnRpbWUuKCpnY0NvbnRyb2xsZXJTdGF0ZSkuZW5saXN0V29ya2VyCCUhL5QgglYWcnVudGltZS5tYXJrcm9vdC5mdW5jMUV26OHD7b5fMWdpdGh1Yi5jb20vZ29vZ2xlL3Bwcm9mL3Byb2ZpbGUuKCpQcm9maWxlKS5lbmNvZGUJylTUF5mfwiNjb21wcmVzcy9mbGF0ZS4oKmRlY29tcHJlc3NvcikuUmVhZGymHc/Ad5l5FnJ1bnRpbWUuKCpnY0JpdHMpLmJpdHBuG0UhoG7aUhJuZXQuKCpuZXRGRCkuQ2xvc2WUzwMNXMAaHUBnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvcHJvdG8uKCpCbG9jaykuRGVjb2RlHnmsbk8kIb9DZ2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvc2NyYXBlLigqc2NyYXBlQ2FjaGUpLmZvckVhY2hTdGFsZcqbwrUCWJhUGHJ1bnRpbWUuZnVuY05hbWVGb3JQcmludEnfDj6hKUGYE3J1bnRpbWUuZ29zY2hlZEltcGzQWzeCfXCmS0FnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqQXJyYXkpLkFwcGVuZJKRmMwzV7kTXWdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9yZWNlaXZlci9weXJvc2NvcGVyZWNlaXZlci9wcHJvZnBhcnNlci4oKnBQcm9mUGFyc2VyKS5QYXJzZVejd+xdAm5oD3J1bnRpbWUudW5sb2NrMs/mZFtkW/RfGXN5bmMuKCpwb29sQ2hhaW4pLnBvcFRhaWzhStFebo1tbw9ydW50aW1lLnNpZ25hbE3luQTS3KLf2hBydW50aW1lLnB1dGVtcHR5uw20YjmvCZI0Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpiYXRjaCkuU2VuZOKaiUS2U93wS2dvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2V4cG9ydGVyL2V4cG9ydGVyaGVscGVyLigqdGltZW91dFNlbmRlcikuc2VuZNnu4qi07LZBC2lvLlJlYWRGdWxsJFWdXKyFNSVgZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyL2NvbXByZXNzLigqRGVjb21wcmVzc29yKS5EZWNvbXByZXNzHaFVOzVEwMgfaW50ZXJuYWwvcG9sbC4oKnBvbGxEZXNjKS5jbG9zZUiIoDDTj8x6GmNvbnRleHQuKCpjYW5jZWxDdHgpLlZhbHVln0cPG3qBY2peZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby9nb2RlbHRhcHJvZi9pbnRlcm5hbC9wcHJvZi4oKkRlbHRhSGVhcFByb2ZpbGVyKS5Xcml0ZUhlYXBQcm90b8hEQut8+1ZbEXJ1bnRpbWUuY29weXN0YWNrj9bbEjO+SeweZ2l0aHViLmNvbS9nby1mYXN0ZXIvY2l0eS5DSDY0purYHU2LD9ZbZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvZXhwb3J0ZXIvZXhwb3J0ZXJoZWxwZXIuKCpsb2dzRXhwb3J0ZXJXaXRoT2JzZXJ2YWJpbGl0eSkuc2VuZHH+xbv30EVRV2dpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9jbGlja2hvdXNlcHJvZmlsZWV4cG9ydGVyL2NoLnJlYWRUcmVlRnJvbU1hcI4mpXyGCYhiGm5ldC4oKlJlc29sdmVyKS50cnlPbmVOYW1lw81YPvZGX+0PcnVudGltZS5nY0RyYWluHx5GrInl0V8Uc3luYy4oKlBvb2wpLmdldFNsb3c32wBr0+tTUBpydW50aW1lLigqX2Z1bmMpLmlzSW5saW5lZOu86rgChxiPEnJ1bnRpbWUuY2FzZ3N0YXR1cwoVOlOd6TwIFnJ1bnRpbWUuZ2NCZ01hcmtXb3JrZXIwrJGHGunHJBdydW50aW1lLigqc3BhblNldCkucHVzaCMxIq/ODe/ZH25ldC9odHRwLigqVHJhbnNwb3J0KS5Sb3VuZFRyaXCxmBYxGXTUcE5nby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci4oKm1ldHJpY3NSZXF1ZXN0KS5FeHBvcnQyr4Hs136ElhVydW50aW1lLmNvbmNhdHN0cmluZ3MZXs0RwXvVozxnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmJhdGNoKS5BcHBlbmRTdHJ1Y3SpLJgF8Kk2myFydW50aW1lL2ludGVybmFsL3N5c2NhbGwuU3lzY2FsbDZX8qr4XFyhxz1naXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9tb2RlbC9sYWJlbHMuTGFiZWxzLlZhbGlkYXRl7kpF6jnp+pAlZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby5udW1HQ40+z8DQ7EZTDHN5c2NhbGwucmVhZB0X50IzkvozD3N5c2NhbGwuU3lzY2FsbDOeTtT1wCTJOWdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY29ubmVjdCkucHJvY2Vzc8GZoQgd+w4NJm5ldC9odHRwLigqVHJhbnNwb3J0KS5xdWV1ZUZvcklkbGVDb25uxpUHgKRer2MbbmV0LigqY29uZikuaG9zdExvb2t1cE9yZGVydwkgMDxfTA4gcnVudGltZS4oKnN0YWNrU2NhblN0YXRlKS5wdXRQdHJrmANg+RUIb5sBZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvZXhwb3J0ZXIvaW50ZXJuYWwvcXVldWUuKCpib3VuZGVkTWVtb3J5UXVldWVbZ28uc2hhcGUuaW50ZXJmYWNlIHsgRXhwb3J0KGNvbnRleHQuQ29udGV4dCkgZXJyb3I7IEl0ZW1zQ291bnQoKSBpbnQgfV0pLkNvbnN1bWV0MrwbQigACXJnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcmVjZWl2ZXIvcHJvbWV0aGV1c3JlY2VpdmVyL2ludGVybmFsLm5vcm1hbGl6ZU1ldHJpY05hbWW4zles2qhBsw9ydW50aW1lLm5ldHBvbGy7ef7F8HpIjBxydW50aW1lLigqcmFuZG9tT3JkZXIpLnN0YXJ02MmrOsxrigoScnVudGltZS5mdXRleHNsZWVwtsamCKjpwF4YcmVmbGVjdC4oKk1hcEl0ZXIpLlZhbHVlO0IOrUg+IDcYcnVudGltZS4oKnVud2luZGVyKS5uZXh0nlQcdLFLsG8hbmV0L2h0dHAuKCpwZXJzaXN0Q29ubikud3JpdGVMb29wZWMdO+8kvD9eZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyLigqcHlyb3Njb3BlUmVjZWl2ZXIpLnJlYWRQcm9maWxlc7pitwbIDeH8DXJ1bnRpbWUuZnV0ZXgobQmR6ppTR15naXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvcmVjZWl2ZXIvcHlyb3Njb3BlcmVjZWl2ZXIuKCpweXJvc2NvcGVSZWNlaXZlcikuaGFuZGxlLmZ1bmMx3t9XWPOguM4UbmV0LigqUmVzb2x2ZXIpLmRpYWwLHi0DfQ4Q2SVuZXQvaHR0cC4oKlRyYW5zcG9ydCkuZGVjQ29ubnNQZXJIb3N0vu+adBsx+N0ZaW50ZXJuYWwvcG9sbC4oKkZEKS5DbG9zZST8H5PkEhr3EnJ1bnRpbWUuZ2NNYXJrRG9uZSN3sRO7YfWmF3J1bnRpbWUuKCpsZnN0YWNrKS5wdXNo6f5Zn3fq/MMPcnVudGltZS5zZWxsb2Nr3Ullk5eLmNgabmV0L2h0dHAuKCpUcmFuc3BvcnQpLmRpYWzZ8UHwc2oWLxJuZXQuKCpuZXRGRCkuV3JpdGUIouedHfF3iElnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci4oKnJldHJ5U2VuZGVyKS5zZW5kVqbWt4FhruU7Z2l0aHViLmNvbS9rbGF1c3Bvc3QvY29tcHJlc3MvZmxhdGUuKCpjb21wcmVzc29yKS5zdG9yZUZhc3RcfPTJKceytRFuZXQuKCpuZXRGRCkuUmVhZBK5UrhZh7COZGdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIuKCptZXRyaWNzRXhwb3J0ZXIpLmV4cG9ydEhpc3RvZ3JhbURhdGFQb2ludHOXDF9mUmr3wBVlbmNvZGluZy9qc29uLk1hcnNoYWwmuGwr0jjZ6xdmbXQuKCpwcCkuaGFuZGxlTWV0aG9kc/GUZOnCfnNKSWdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpBcnJheSkuYXBwZW5kUm93UGxhaW6Bxhxw6i+TyiVnaXRodWIuY29tL2dvLWZhc3Rlci9jaXR5Lmhhc2gxMjh0bzY0QiQCXQ4hhvlFZ2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvc2NyYXBlLigqc2NyYXBlTG9vcCkuYWRkUmVwb3J0U2FtcGxlQpvOrnEC5VIPcmVmbGVjdC5jb3B5VmFsCMBLK4vtiIdDZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby9nb2RlbHRhcHJvZi4oKkhlYXBQcm9maWxlcikuUHJvZmlsZUqUKUqov2wwC2ZtdC5TcHJpbnRmA4IjfMbKsjAsZ28ub3BlbnRlbGVtZXRyeS5pby9vdGVsL21ldHJpYy5OZXdBZGRDb25maWeZIzYj7wRlxllnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL2dvZGVsdGFwcm9mL2ludGVybmFsL3Bwcm9mLigqcHJvZmlsZUJ1aWxkZXIpLmVtaXRMb2NhdGlvbim85OsI2hUEemdpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9yZWNlaXZlci9wcm9tZXRoZXVzcmVjZWl2ZXIvaW50ZXJuYWwuKCp0cmFuc2FjdGlvbikuZ2V0U2VyaWVzUmVm0si9Ks1nFuEUcnVudGltZS5hY3F1aXJlU3Vkb2fkKCgBqHtIfCtjb21wcmVzcy9mbGF0ZS4oKmRlY29tcHJlc3NvcikuaHVmZm1hbkJsb2NryWJlQEijMRNGZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyLmdldE5vZGVJZMhx+SDM9W3VGGJ5dGVzLigqQnVmZmVyKS5SZWFkRnJvbexJ/SlemEzmHHJ1bnRpbWUuKCpzd2VlcExvY2tlZCkuc3dlZXCuSdmPZWDEFw1zeXNjYWxsLndyaXRlnU+wX8Z8emQicnVudGltZS5nY0RyYWluTWFya1dvcmtlckRlZGljYXRlZCb3WRNj/VQOYGdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIuKCptZXRyaWNzRXhwb3J0ZXIpLmV4cG9ydE51bWJlckRhdGFQb2ludADZ/f1srDfYEXJ1bnRpbWUubmFub3RpbWUx2SMJe5MdJQMWcnVudGltZS4oKkZyYW1lcykuTmV4dEmhqZnHx1txG3J1bnRpbWUuc3RhcnRUaGVXb3JsZC5mdW5jMeUN3pY1xs0ND3J1bnRpbWUuZ29leGl0MAS4FSgb5rv7F3J1bnRpbWUuZ2NGbHVzaEJnQ3JlZGl04e4jK3fHv/wQcnVudGltZS5tYWtlY2hhboadauvDVYG2TGdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9yZWNlaXZlci9weXJvc2NvcGVyZWNlaXZlci5wb3N0UHJvY2Vzc1Byb2b60DLvY59YcxRidWZpby4oKlJlYWRlcikuUmVhZBIyaT2sTm02HXJ1bnRpbWUudHlwZVBvaW50ZXJzLm5leHRGYXN0OHpnJxRQo9UTcnVudGltZS5mdXRleHdha2V1cB9fhMihwiuHFXJ1bnRpbWUucmVzZXRzcGlubmluZ9h6vT2sovqCKGNvbXByZXNzL2ZsYXRlLigqZGVjb21wcmVzc29yKS5uZXh0QmxvY2uklodLQiZOKR9ydW50aW1lLigqbWNlbnRyYWwpLnVuY2FjaGVTcGFuhf2sWhQGelsUcnVudGltZS5uZXh0RnJlZUZhc3TFHoEuYjXMJj5naXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmNvbm5lY3QpLnByZXBhcmVCYXRjaJpOF0s2FVcjDXJ1bnRpbWUubVBhcmvIOpuAZcamARFuZXQuKCpjb25uKS5Xcml0ZXuE4dG3R8qoFW5ldC4oKkROU0Vycm9yKS5FcnJvckHvrBFnAPpLJ2dpdGh1Yi5jb20vZ29vZ2xlL3Bwcm9mL3Byb2ZpbGUubWFyc2hhbNsCUqI4lqTEdXR5cGU6LmVxLmdpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9yZWNlaXZlci9wcm9tZXRoZXVzcmVjZWl2ZXIvaW50ZXJuYWwudGltZXNlcmllc0tleV4zxa+wnSydFHJ1bnRpbWUubVByb2ZfTWFsbG9jfLkgt7XukjZcZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci4oKm1ldHJpY3NFeHBvcnRlcikuY29sbGVjdEZyb21NZXRyaWPrnTnzCtjb6Q9ydW50aW1lLmJnc3dlZXC1yamdEB/rXBduZXQuKCpjb25mKS5sb29rdXBPcmRlcoLzzCq5bhGqEHN5bmMuKCpQb29sKS5HZXTEdEZMwm8PmBtydW50aW1lLmVudGVyc3lzY2FsbF9zeXNtb277YvJAWynYhQpuZXQuc29ja2V01LLYi9MFnghKZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvZXhwb3J0ZXIvZXhwb3J0ZXJoZWxwZXIuKCpPYnNSZXBvcnQpLnN0YXJ0T3AYB0RLGoaC91dnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9pbnRlcm5hbC9mYW5vdXRjb25zdW1lci4oKm1ldHJpY3NDb25zdW1lcikuQ29uc3VtZU1ldHJpY3PxFldvdlpyIjRnby5vcGVudGVsZW1ldHJ5LmlvL290ZWwvc2RrL21ldHJpYy4oKmludDY0SW5zdCkuQWRkUcLOkyK4IFw3Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpzdHJ1Y3RNYXApLk1hcFo7N8lyIvz1EHJ1bnRpbWUuZnVuY25hbWVMEjAdzoTyyBZydW50aW1lLmdvc3RyaW5nbm9jb3B5Ye/KEDsyGXhaZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL2NsaWNraG91c2Vwcm9maWxlZXhwb3J0ZXIvY2guKCpMaW1pdGVkUG9vbCkuZ2V0iTs1oz8F4vQVcnVudGltZS5zdGFydFRoZVdvcmxk+A3JZvs1GHM4Z2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby4oKlNlc3Npb24pLnRha2VTbmFwc2hvdHOp9WvCrWav0hBydW50aW1lLm1hbGxvY2djGTyGyxPnTcREZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL2NvbHVtbi4oKkFycmF5KS5BcHBlbmRSb3c7tksjWy+HNjZnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmJhdGNoKS5BcHBlbmTK7WWDBPYU4BVydW50aW1lLm1hcmtyb290QmxvY2vmpiw1bB8MGyBuZXQvaHR0cC5wZXJzaXN0Q29ubldyaXRlci5Xcml0ZYXV91blegKkOmdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28uKCpTZXNzaW9uKS5kdW1wSGVhcFByb2ZpbGX3fgQ8CY1Z0hFydW50aW1lLmdvc2NoZWRfbZtEzD27nXWJhAFnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcmVjZWl2ZXIvcHJvbWV0aGV1c3JlY2VpdmVyL2ludGVybmFsLigqaW5pdGlhbFBvaW50QWRqdXN0ZXIpLkFkanVzdE1ldHJpY3NYxSWtgwP59RFuZXQuKCpjb25uKS5DbG9zZWsXX88NmXk/WmdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9jbGlja2hvdXNlcHJvZmlsZWV4cG9ydGVyL2NoLigqTGltaXRlZFBvb2wpLnB1dFCuScdKML1KGnJ1bnRpbWUubWFwYWNjZXNzMl9mYXN0c3Ry9BVzNOPRxEwVcnVudGltZS5jb25jYXRzdHJpbmcyXSRreh31AiYfZW5jb2RpbmcvanNvbi5tYXBFbmNvZGVyLmVuY29kZXRLNkBkGLfKPWdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS5uZXdTY3JhcGVQb29sLmZ1bmMxLjFNlHYu16zQ4BhydW50aW1lLigqZ2NXb3JrKS50cnlHZXRrUeA/GEbNFBNydW50aW1lLnN5c3RlbXN0YWNrLE84EskYf7laZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci4oKm1ldHJpY3NFeHBvcnRlcikucHVzaE1ldHJpY3NEYXRhUL2517YSKW5dZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci4oKm1ldHJpY3NFeHBvcnRlcikuY29sbGVjdEZyb21NZXRyaWNz2+W+z3C82iUaaW50ZXJuYWwvcG9sbC4oKkZEKS5kZWNyZWZsgrCkBxfJlR9ydW50aW1lLigqZ2NCaXRzQXJlbmEpLnRyeUFsbG9jA3w01sBQXT5ZZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby9nb2RlbHRhcHJvZi9pbnRlcm5hbC9wcHJvZi4oKnByb2ZpbGVCdWlsZGVyKS5Mb2NzRm9yU3RhY2s3wHHL99E4NhVuZXQvaHR0cC4oKkNsaWVudCkuZG9yCYA3fv08HhluZXQuKCpEaWFsZXIpLkRpYWxDb250ZXh0KKwanumCLDsQcnVudGltZS5zd2VlcG9uZXQ2oG4eJ2UIIXJ1bnRpbWUuKCpzd2VlcExvY2tlcikudHJ5QWNxdWlyZRmstVaSifi2O2dpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY29ubmVjdCkuc2VuZFF1ZXJ5VV0EG3Xuomc/Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjb25uZWN0KS5wcm9maWxlRXZlbnRzA07FIAXCfgI9Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL2NvbHVtbi5UeXBlLkNvbHVtbueliUC04llAFXJ1bnRpbWUuKCptc3BhbikuYmFzZbUWby3gY8wzJXJ1bnRpbWUuKCptc3BhbikuaGVhcEJpdHNTbWFsbEZvckFkZHIgjRBgIpsaAzZnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvLigqU2Vzc2lvbikuU3RhcnQuZnVuYzFz1MV0tStPajhnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmNvbm5lY3QpLmhhbmRsZQZHhw4jhC/nE3N5c2NhbGwuUmF3U3lzY2FsbDZF9l2ngBqtURhydW50aW1lLnNwYW5DbGFzcy5ub3NjYW74fblWbXAQew1uZXQuc3lzU29ja2V0r/ovaQhDtVIObmV0LmluaXQuZnVuYzE9+AdvzlPoji1naXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLmVuY29kZU1lc3NhZ2VclEI3HV3KfxRydW50aW1lLlJlYWRNZW1TdGF0c9EN7qUbgg5zGHJ1bnRpbWUuKCptY2FjaGUpLnJlZmlsbHrAZw9cMSKoEHJ1bnRpbWUuc2NoZWR1bGX4jTWPAFYzLA9ydW50aW1lLkNhbGxlcnN9SSEXICR1eERnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqVHVwbGUpLkFwcGVuZFJvd4bhCcQ0wn5fEXJ1bnRpbWUubmV3b2JqZWN0uDFBPMlZScsYcnVudGltZS53YkJ1ZkZsdXNoLmZ1bmMxVotoGob81nodbmV0L2h0dHAuKCpUcmFuc3BvcnQpLmdldENvbm6AsTb/INmQPpYBZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvZXhwb3J0ZXIvaW50ZXJuYWwvcXVldWUuKCpDb25zdW1lcnNbZ28uc2hhcGUuaW50ZXJmYWNlIHsgRXhwb3J0KGNvbnRleHQuQ29udGV4dCkgZXJyb3I7IEl0ZW1zQ291bnQoKSBpbnQgfV0pLlN0YXJ0LmZ1bmMxoLv7kQTRG34UcmVmbGVjdC50eXBlZG1lbW1vdmW7lIrB/cuOthRydW50aW1lLigqYnVja2V0KS5tcMKBhVLB30bGNWdvLm9wZW50ZWxlbWV0cnkuaW8vb3RlbC90cmFjZS5TcGFuQ29udGV4dEZyb21Db250ZXh0XGZemHua7WImY29tcHJlc3MvZmxhdGUuKCpkZWNvbXByZXNzb3IpLmh1ZmZTeW0VFqboGj/c+xRydW50aW1lLnR5cGVkbWVtbW92ZckmZwlu+971E3J1bnRpbWUuZnVuY3NwZGVsdGFG3wEYthNxTTdnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmNvbm5lY3QpLmZsdXNoaj4O5cDsUyAMc3lzY2FsbC5SZWFkbeT/UCez9IERcnVudGltZS5jaGVhcHJhbmQEDWVNOEGsWTxnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmJhdGNoQ29sdW1uKS5BcHBlbmSMXcaD5qvkgg1zeXNjYWxsLldyaXRl8beyuRhUkCkkZW5jb2RpbmcvanNvbi4oKmVuY29kZVN0YXRlKS5tYXJzaGFsPACS2Cyzf5sQcnVudGltZS5tYXJrcm9vdOHey7tJYAjic2dpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9yZWNlaXZlci9wcm9tZXRoZXVzcmVjZWl2ZXIvaW50ZXJuYWwuKCp0aW1lc2VyaWVzTWFwKS5nZXSBlEA+4GT1S09nby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9wcm9jZXNzb3IvcHJvY2Vzc29yaGVscGVyLigqT2JzUmVwb3J0KS5yZWNvcmREYXRhbYz6VAS7xClLZ2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvc2NyYXBlLigqc2NyYXBlTG9vcCkuc2NyYXBlQW5kUmVwb3J0LmZ1bmMx/8+Qrm2l9xwRcnVudGltZS5zY2FuYmxvY2tPB7B3HnuMz1Rnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9wcm9jZXNzb3IvcHJvY2Vzc29yaGVscGVyLigqT2JzUmVwb3J0KS5NZXRyaWNzQWNjZXB0ZWRMDfiNqq00vwtydW50aW1lLmFkZLxRhWFKG8DLHW5ldC4oKnN5c0RpYWxlcikuZGlhbFBhcmFsbGVsBjEVq3pWQggScnVudGltZS5tYXBhY2Nlc3My24Usp2X26F0zZ2l0aHViLmNvbS9rbGF1c3Bvc3QvY29tcHJlc3MvZmxhdGUuKCpXcml0ZXIpLkNsb3Nljw4KfGrLB/kZcnVudGltZS5tYXJrQml0cy5pc01hcmtlZLEnCRb3aswWEnJ1bnRpbWUubm90ZXdha2V1cC2Jv/gqaHzfS2dpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpBcnJheSkuYXBwZW5kUm93RGVmYXVsdM6m/PsaRIKLMmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jaC1nby9wcm90by4oKkNvbFN0cikuQXBwZW5kdr07JCLDrKo6Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjb25uZWN0KS5zZW5kRGF0YVWsb1IeyqqFUGdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIucmVtb3ZlUHJvbUZvcmJpZGRlblJ1bmVz1sPC0O0uUCoScnVudGltZS5maW5kT2JqZWN02lXP3hMDr7gQcnVudGltZS5wcmVlbXB0TX0klkEBee04FGNvbnRleHQuV2l0aERlYWRsaW5loG0fwR3d0gF0Z2l0aHViLmNvbS9vcGVuLXRlbGVtZXRyeS9vcGVudGVsZW1ldHJ5LWNvbGxlY3Rvci1jb250cmliL3JlY2VpdmVyL3Byb21ldGhldXNyZWNlaXZlci9pbnRlcm5hbC4oKnRyYW5zYWN0aW9uKS5Db21taXQJmb8AxgyZAA9ydW50aW1lLmV4ZWN1dGVrZ1QQxrZBpxFydW50aW1lLmNtcHN0cmluZyjjYW55iNufhQFnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcmVjZWl2ZXIvcHJvbWV0aGV1c3JlY2VpdmVyL2ludGVybmFsLigqdHJhbnNhY3Rpb24pLmdldE9yQ3JlYXRlTWV0cmljRmFtaWx5CTpQu2qi9awVYnVmaW8uKCpXcml0ZXIpLkZsdXNobGT77l/GDOMSc3RyaW5ncy5GaWVsZHNGdW5jiC+KiUHiLbM6Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjb25uZWN0KS5yZWFkRGF0YV1AROHqYOYLKWdvbGFuZy5vcmcveC9uZXQvdHJhY2UuKCpldmVudExvZykucHJpbnRm8LEtItfVamtGZ2l0aHViLmNvbS9wcm9tZXRoZXVzL2NvbW1vbi9jb25maWcuKCp1c2VyQWdlbnRSb3VuZFRyaXBwZXIpLlJvdW5kVHJpcNWnoFkEPj244AJnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL2dvZGVsdGFwcm9mL2ludGVybmFsL3Bwcm9mLigqcHJvZk1hcFtnby5zaGFwZS5zdHJ1Y3QgeyBnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL2dvZGVsdGFwcm9mL2ludGVybmFsL3Bwcm9mLmFsbG9jT2JqZWN0cyBpbnQ2NCB9LGdvLnNoYXBlLnN0cnVjdCB7IGdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vZ29kZWx0YXByb2YvaW50ZXJuYWwvcHByb2YuYWxsb2NPYmplY3RzIGludDY0OyBnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL2dvZGVsdGFwcm9mL2ludGVybmFsL3Bwcm9mLmludXNlT2JqZWN0cyBpbnQ2NCB9XSkuTG9va3VwlKTvgxZLaOQeZ28udWJlci5vcmcvemFwLigqTG9nZ2VyKS5XYXJuupFG1qZepzEYbmV0Ligqc3lzRGlhbGVyKS5kaWFsVURQlJ5+Opyn4BwQcnVudGltZS5nZGVzdHJvefMDAAAAAAAAAABx/voy1A56tZqqYiQgRv0AAAAAAAAAAACAlpgAAAAAAAAAAAAAAAAAGEQvj3jClhEGgrwfjqHvAAAAAAAAAAAAAFpiAgAAAAAAAAAAAAAAAICxNv8g2ZA+NToZfqc4+AAAAAAAAAAAAADC6wsAAAAAAAAAAAAAAAAobQmR6ppTR27iu9hcprwAAAAAAAAAAACA8PoCAAAAAAAAAAAAAAAArgcmiCQdJhHYVgzshpPoAAAAAAAAAAAAgJaYAAAAAAAAAAAAAAAAAAoVOlOd6TwIXEXnPu9i8AAAAAAAAAAAAIAzAjsAAAAAAAAAAAAAAAAA2f39bKw32E5dnanwGfMAgJaYAAAAAACAlpgAAAAAAAAAAAAAAAAAII0QYCKbGgPo5/BkxmuOAAAAAAAAAAAAALTEBAAAAAAAAAAAAAAAAIG//wcmlW+VzGtdZEkKkgAAAAAAAAAAAABoiQkAAAAAAAAAAAAAAACeVBx0sUuwb0cvpyMsVu4AAAAAAAAAAAAALTEBAAAAAAAAAAAAAAAAlLf3+v5y09GAA/yGjqfKAAAAAAAAAAAAgLLmDgAAAAAAAAAAAAAAAOudOfMK2NvpZYzIb9G1pQAAAAAAAAAAAAAtMQEAAAAAAAAAAAAAAAAEjeHw9nxiukwec0Lew84AAAAAAAAAAAAALTEBAAAAAOjn8GTGa44A+A3JZvs1GHO30o44O6AZAQAAAAAAAAAAALTEBAAAAADMa11kSQqSAGzG8IPhmf1qVPno2up7MwEAAAAAAAAAAABoiQkAAAAAZYzIb9G1pQAorBqe6YIsO1Lbd22GbEoBAAAAAAAAAAAALTEBAAAAAG7iu9hcprwAZWMdO+8kvD9G6ho9FgsfAQAAAAAAAAAAgPD6AgAAAACAA/yGjqfKAI0cOtJTIknQFxiNbq2SOAEAAAAAAAAAAID+IQoAAAAAgAP8ho6nygD3fgQ8CY1Z0se5ANcm5kEBAAAAAAAAAACA8PoCAAAAAIAD/IaOp8oA5Q3eljXGzQ2dBvJkesRnAQAAAAAAAAAAgMPJAQAAAABMHnNC3sPOAAseLQN9DhDZIRVM2poVBwGAlpgAAAAAAICWmAAAAAAATB5zQt7DzgBiMQJUAbl+3SmnfQ+9kSwBAAAAAAAAAACAlpgAAAAAANhWDOyGk+gA80cdjE5muurerExaETxXAQAAAAAAAAAAgJaYAAAAAABHL6cjLFbuAAk6ULtqovWsn2v/OzTKXQEAAAAAAAAAAAAtMQEAAAAABoK8H46h7wCOJqV8hgmIYp0X20qJ9g4BAAAAAAAAAAAAWmICAAAAAFxF5z7vYvAAJPwfk+QSGvcXF8prputAAQAAAAAAAAAAgJaYAAAAAABcRec+72LwAGtR4D8YRs0UrYUtqZaFYwEAAAAAAAAAAACdaToAAAAANToZfqc4+ABrmANg+RUIbzM+2+qGyWgBAAAAAAAAAAAAwusLAAAAAJqqYiQgRv0AiISHXT1td4Q3Od0DnKozAQAAAAAAAAAAgJaYAAAAAACdF9tKifYOAeAoxjhu5LK46ig6MIbssAEAAAAAAAAAAABaYgIAAAAAt9KOODugGQFx62zG+6+fS0ZkU0/Qi/EBAAAAAAAAAACAHSwEAAAAALfSjjg7oBkB80cdjE5muurH2OOJni3bAQAAAAAAAAAAgJaYAAAAAABG6ho9FgsfASRVnVyshTUlnD/Fo/KCzwEAAAAAAAAAAAAtMQEAAAAARuoaPRYLHwGGnWrrw1WBthPb/5wJtOoBAAAAAAAAAACAlpgAAAAAAEbqGj0WCx8BVq+T63lfgbUeGz1+iJydAQAAAAAAAAAAgJaYAAAAAABG6ho9FgsfAZKRmMwzV7kTey1lJe8tvQEAAAAAAAAAAICWmAAAAAAAKad9D72RLAHdSWWTl4uY2P9hZfmVF9gBAAAAAAAAAACAlpgAAAAAAFT56NrqezMBKiYD0avBXyOAS0njYU++AQAAAAAAAAAAgMPJAQAAAABU+eja6nszAX0klkEBee04lCs+BgpMqwEAAAAAAAAAAICWmAAAAAAAVPno2up7MwFtjPpUBLvEKaTaJle/CoABAAAAAAAAAAAAWmICAAAAAFT56NrqezMBz1UgT/pt6GLdMV16wP/NAQAAAAAAAAAAgMPJAQAAAABU+eja6nszAdQ4biXbXBBZFUG8VMcn1AEAAAAAAAAAAIDw+gIAAAAANzndA5yqMwGv+i9pCEO1UqyrRIHyycABAAAAAAAAAACAlpgAAAAAABcYjW6tkjgBesBnD1wxIqjNsxASRUTmAQAAAAAAAAAAgP4hCgAAAAAXF8prputAAZUYf1XL2xlNWqwIhyO+mQEAAAAAAAAAAICWmAAAAAAAx7kA1ybmQQFJ3w4+oSlBmJe3Tb9dQacBAAAAAAAAAACA8PoCAAAAAFLbd22GbEoB7En9KV6YTOao8/net5GDAQAAAAAAAAAAgJaYAAAAAABS23dthmxKAXQ2oG4eJ2UIS2Napr915gGAlpgAAAAAAICWmAAAAAAA3qxMWhE8VwHp/lmfd+r8w6gdDE8dqKABgJaYAAAAAACAlpgAAAAAAJ9r/zs0yl0B5qYsNWwfDBuBjp9I0vvSAQAAAAAAAAAAAC0xAQAAAACthS2ploVjAZ1PsF/GfHpkLgxXxTfG2gEAAAAAAAAAAAA4nBwAAAAArYUtqZaFYwGhDvLntYM3bt4JtofbZNsBAAAAAAAAAAAAZc0dAAAAAJ0G8mR6xGcBesBnD1wxIqhXtmE7fOb3AQAAAAAAAAAAAC0xAQAAAACdBvJkesRnAZSefjqcp+AcLqEoo6Z4oQEAAAAAAAAAAICWmAAAAAAAMz7b6obJaAE27TmsGdFln4LoMbJqwf8BAAAAAAAAAAAAwusLAAAAAKTaJle/CoABoG0fwR3d0gE9XJaKFlRjAgAAAAAAAAAAAFpiAgAAAACo8/net5GDAWyCsKQHF8mVYdeP2X9QIQKAlpgAAAAAAICWmAAAAAAAWqwIhyO+mQHkbq4fBFpukJ+6F6VorD0CAAAAAAAAAACAlpgAAAAAAB4bPX6InJ0BpsYLicns+kSfzrWE74B9AgAAAAAAAAAAgJaYAAAAAAAuoSijpnihAVo7N8lyIvz1v5IaK9JhFAIAAAAAAAAAAICWmAAAAAAAl7dNv11BpwGlYkFtlSd0BItswgxNVi8CAAAAAAAAAACAlpgAAAAAAJe3Tb9dQacBesBnD1wxIqiXDPGTH8xYAgAAAAAAAAAAAFpiAgAAAACUKz4GCkyrASKliK2s2Xly16vzoWB6ZwIAAAAAAAAAAICWmAAAAAAA6ig6MIbssAGCjgKInJ3bMDoIkdhK3mgCAAAAAAAAAAAALTEBAAAAAOooOjCG7LABWMUlrYMD+fU+gVU+TcdvAgAAAAAAAAAAgJaYAAAAAADqKDowhuywAd7fV1jzoLjOZd/l9gtQJwIAAAAAAAAAAICWmAAAAAAAey1lJe8tvQF+EV/AEe3NPuivy98mYGICAAAAAAAAAACAlpgAAAAAAIBLSeNhT74BN8Bxy/fRODau/f3fDwh6AgAAAAAAAAAAgMPJAQAAAACsq0SB8snAAcyF1ON9gI7aRg0bqFVnSAIAAAAAAAAAAICWmAAAAAAA3TFdesD/zQH3ctP2qgjz6Ckbcs1NhGUCAAAAAAAAAACAw8kBAAAAAJw/xaPygs8BGM3/cHirFUn84iUMYhwGAgAAAAAAAAAAAC0xAQAAAACBjp9I0vvSAcg6m4BlxqYB7ISU4xJSMwIAAAAAAAAAAAAtMQEAAAAAFUG8VMcn1AEeeaxuTyQhvzkHkehXBRACgJaYAAAAAAAALTEBAAAAABVBvFTHJ9QBdEs2QGQYt8rD14TXcv1eAgAAAAAAAAAAgJaYAAAAAAAVQbxUxyfUAU1s9+Ub8u/DkMiG9ehLZQIAAAAAAAAAAICWmAAAAAAAFUG8VMcn1AFX8qr4XFyhxx8Y0nbq5UwCAAAAAAAAAACAlpgAAAAAAP9hZfmVF9gBM77u2s/L+2HgNrqoXbZ8AgAAAAAAAAAAgJaYAAAAAAAuDFfFN8baAcPNWD72Rl/tDUL0OGNzGwIAWmICAAAAAAA4nBwAAAAAx9jjiZ4t2wHSyL0qzWcW4QU4yMpr/zcCgJaYAAAAAACAlpgAAAAAAN4JtofbZNsBw81YPvZGX+0CZdppXXFCAoAdLAQAAAAAAGXNHQAAAADNsxASRUTmAQM3qLGFhYD7iLbbtCVhHQIAAAAAAAAAAAAtMQEAAAAAzbMQEkVE5gEfX4TIocIrhx8Vmgk7eyUCAAAAAAAAAACAlpgAAAAAAM2zEBJFROYB1+WHhhMaBcZ2q1IFKWNLAgAAAAAAAAAAADtYCAAAAAAT2/+cCbTqAcliZUBIozETGryp8UvePgIAAAAAAAAAAICWmAAAAAAARmRTT9CL8QEQR4RwgXBT0I6QZRe0uT8CAAAAAAAAAACAHSwEAAAAAFe2YTt85vcB1+WHhhMaBcb30YpZJgFTAoCWmAAAAAAAAC0xAQAAAACC6DGyasH/AR52s1uz7A0DEYH0KA+OAQIAAAAAAAAAAADC6wsAAAAAEYH0KA+OAQJk1WpYsh4gMZAZHjHtUtQCAAAAAAAAAACA0fAIAAAAABGB9CgPjgECpurYHU2LD9bYaIc2XRTaAgAAAAAAAAAAgPD6AgAAAAD84iUMYhwGAshx+SDM9W3VpT4hIGuhvAIAAAAAAAAAAAAtMQEAAAAAOQeR6FcFEAJMDfiNqq00v6TDI1rgINQCgJaYAAAAAACAlpgAAAAAAL+SGivSYRQCTBIwHc6E8sg/ASGArFK1AgAAAAAAAAAAgJaYAAAAAAANQvQ4Y3MbAiWNw7QPJRzdL1IED3JG3gKAlpgAAAAAAICWmAAAAAAADUL0OGNzGwL317MU8G4OJvPSDBCDdqgCAAAAAAAAAACAw8kBAAAAAA1C9DhjcxsCPACS2Cyzf5smQ0IcySywAgAAAAAAAAAAAFpiAgAAAAANQvQ4Y3MbAgS4FSgb5rv7qXfyQb2mswKAlpgAAAAAAICWmAAAAAAADUL0OGNzGwIUymKjbGZpbt3/8KmD3PYCgKS/BwAAAACAk9wUAAAAAIi227QlYR0CV6N37F0CbmgpFxAYyMGvAgAtMQEAAAAAAC0xAQAAAAAfFZoJO3slAqViQW2VJ3QEjvb1TjUC3QIAAAAAAAAAAICWmAAAAAAAZd/l9gtQJwJyCYA3fv08HpnRgvpwX+ECAAAAAAAAAACAlpgAAAAAAItswgxNVi8CcAvEyvhf2dBCJk+jK4q1AgAAAAAAAAAAgJaYAAAAAADshJTjElIzAtnxQfBzahYvnkU3srLw0wIAAAAAAAAAAAAtMQEAAAAAn7oXpWisPQJVjVMQssvOnqi5RuMAhrkCAAAAAAAAAACAlpgAAAAAABq8qfFL3j4Cj9bbEjO+Sext3nruta6HAgAAAAAAAAAAgJaYAAAAAACOkGUXtLk/Au5KReo56fqQ/CbFJpSe+QIAAAAAAAAAAICWmAAAAAAAjpBlF7S5PwKF1fdW5XoCpKIh/zOMuIUCAAAAAAAAAAAAh5MDAAAAAAJl2mldcUIC99ezFPBuDiZkNVjAhLm6AgAAAAAAAAAAgEpdBQAAAAACZdppXXFCAigpYBbs7bb8kt1iDXyghgIAAAAAAAAAAICWmAAAAAAAAmXaaV1xQgJF9l2ngBqtUYsv5rcrnZMCgJaYAAAAAACAlpgAAAAAAAJl2mldcUICTZR2Ltes0ODLG6aQ6j7AAgAAAAAAAAAAgMPJAQAAAAACZdppXXFCAhTKYqNsZmluE4K7s142ogKAd44GAAAAAIAMSREAAAAARg0bqFVnSALGlQeApF6vY5KlLWgT/tACAAAAAAAAAACAlpgAAAAAAHarUgUpY0sC1dcTWLt4WMrIvzwRFmmVAgAAAAAAAAAAgJaYAAAAAAB2q1IFKWNLArjOV6zaqEGz7GYY5Zg56wIAAAAAAAAAAABaYgIAAAAAdqtSBSljSwLzLGVCz1aQq+5JGbKqVpACgJaYAAAAAACAlpgAAAAAAHarUgUpY0sCmk4XSzYVVyNnsggZNLSZAgAAAAAAAAAAAFpiAgAAAAB2q1IFKWNLAmEDOGnJKYBG/sMzgRia0gKAlpgAAAAAAICWmAAAAAAAdqtSBSljSwKMoFlkje2cCAij1vXKdrUCgMPJAQAAAACAw8kBAAAAAB8Y0nbq5UwCVRtgDuujnTvXjathR6m/AgAAAAAAAAAAgJaYAAAAAAD30YpZJgFTAhR4cW6/VG5pUqlfvxXUkAKAlpgAAAAAAICWmAAAAAAAlwzxkx/MWALX5YeGExoFxsfFsN65evwCAAAAAAAAAACAw8kBAAAAAJcM8ZMfzFgCCZm/AMYMmQBmvA5oHyiZAgAAAAAAAAAAgJaYAAAAAADD14TXcv1eAo8Fh2IVyZG5XzqYRpnQvgIAAAAAAAAAAICWmAAAAAAA6K/L3yZgYgJB76wRZwD6SxolkI7fLZ4CAAAAAAAAAACAlpgAAAAAAD1clooWVGMCm0TMPbuddYnqDQRlDpfSAgAAAAAAAAAAgJaYAAAAAAA9XJaKFlRjAhgHREsahoL3bC76ZIAzsAIAAAAAAAAAAIDDyQEAAAAAkMiG9ehLZQIpvOTrCNoVBFyu7MVvy8ECAAAAAAAAAACAlpgAAAAAACkbcs1NhGUCQiQCXQ4hhvnoLTTWN9rVAgAAAAAAAAAAgMPJAQAAAADXq/OhYHpnAobhCcQ0wn5fMu2Kdx8M2AIAAAAAAAAAAICWmAAAAAAAOgiR2EreaALIOpuAZcamAchihfgn9boCAAAAAAAAAAAALTEBAAAAAD6BVT5Nx28CbhtFIaBu2lIJNX0oclifAgAAAAAAAAAAgJaYAAAAAACu/f3fDwh6AtUdZfSJPqEUzWAQcRoMtwIAAAAAAAAAAIDDyQEAAAAA4Da6qF22fAKeIu8KUN1k1EDcQuild54CAAAAAAAAAACAlpgAAAAAAJ/OtYTvgH0CqfVrwq1mr9LyrlifUEqnAgAAAAAAAAAAgJaYAAAAAACiIf8zjLiFAgjASyuL7YiHlb24Z0jWGAMAAAAAAAAAAACHkwMAAAAAkt1iDXyghgK4MUE8yVlJywKbAgM9l2sDgJaYAAAAAACAlpgAAAAAAG3eeu61rocCgcYccOovk8pq5gBSql97A4CWmAAAAAAAgJaYAAAAAADIvzwRFmmVAiN3sRO7YfWmMUNditabSwOAlpgAAAAAAICWmAAAAAAAZrwOaB8omQLrvOq4AocYjwU2QGBVh3ADgJaYAAAAAACAlpgAAAAAAGeyCBk0tJkC0LWYMYKV2DTUMbXCQRcuAwAAAAAAAAAAAFpiAgAAAAAaJZCO3y2eAkV26OHD7b5fD4UExzU4IgMAAAAAAAAAAICWmAAAAAAAQNxC6KV3ngL3+4b2W3DI/hRejamHkU4DAAAAAAAAAACAlpgAAAAAAAk1fShyWJ8Cvu+adBsx+N03ZwEZAOtdAwAAAAAAAAAAgJaYAAAAAAATgruzXjaiAhD8kMwOtjQ7OaB/ERUoMwOAlpgAAAAAAICWmAAAAAAAE4K7s142ogJsph3PwHeZefRwG25EVFsDAC0xAQAAAAAALTEBAAAAABOCu7NeNqIC56WJQLTiWUDmECsTGEBHAwAtMQEAAAAAAC0xAQAAAAATgruzXjaiAo8OCnxqywf5Qo003DxlJQOAlpgAAAAAAICWmAAAAAAAE4K7s142ogKKXI++6cx0eu7eurvqS0cDAC0xAQAAAAAALTEBAAAAABOCu7NeNqIC+42Xo+4DpTnURrVBskc4AwAtMQEAAAAAAC0xAQAAAAATgruzXjaiApD7dVaWltdX+sY6U31fUAOAlpgAAAAAAICWmAAAAAAAE4K7s142ogJqIJ43Admxy7elNHE/LS4DgJaYAAAAAACAlpgAAAAAABOCu7NeNqIC1sPC0O0uUCoSFSMqr2MnAwBaYgIAAAAAAFpiAgAAAAATgruzXjaiAhIyaT2sTm02o16OqDm5ZQMALTEBAAAAAAAtMQEAAAAA8q5Yn1BKpwLK1qreFSyTU2b4NbCLZxkDAAAAAAAAAACAlpgAAAAAAPPSDBCDdqgCoGeigSXSXr+b8Z0crjYkAwAAAAAAAAAAAC0xAQAAAADz0gwQg3aoAqui8EyD3+S2IZF+D84iEQMAAAAAAAAAAICWmAAAAAAAJkNCHMkssALK7WWDBPYU4F+dWEKDcx4DAAAAAAAAAACAw8kBAAAAACZDQhzJLLACCCUhL5QgglZDX/obY6hiAwAAAAAAAAAAgJaYAAAAAABsLvpkgDOwAsVOi6ZAhQeyHW2ne0nAHAOAlpgAAAAAAIDDyQEAAAAAPwEhgKxStQIV5+nLyvXLSFlU/OvgBToDAAAAAAAAAACAlpgAAAAAAEImT6MrirUCsScJFvdqzBaqmQ/H5CIdAwAAAAAAAAAAgJaYAAAAAADNYBBxGgy3AvmqKJXaA3cYEE93KO8xHwMAAAAAAAAAAIDDyQEAAAAAqLlG4wCGuQK7lIrB/cuOtv+KIdrJszUDgJaYAAAAAACAlpgAAAAAAGQ1WMCEuboCq6LwTIPf5LYHCvWdExomAwAAAAAAAAAAgJaYAAAAAABkNVjAhLm6AqBnooEl0l6/b/8zQXSDBQMAAAAAAAAAAAC0xAQAAAAAyGKF+Cf1ugLZ8UHwc2oWL6SX7/kPy0YDAAAAAAAAAAAALTEBAAAAAKU+ISBrobwCvLBqXa/OR4oac8jwlqFyAwAAAAAAAAAAAC0xAQAAAABfOphGmdC+AlfbWLBj/r1qh9/3HC3JNAMAAAAAAAAAAICWmAAAAAAA142rYUepvwIBnPURAakjAjWHf4c1FUIDgJaYAAAAAACAlpgAAAAAAMsbppDqPsACdWeYuFSqNqNPdgu+NdIiAwAtMQEAAAAAAC0xAQAAAADLG6aQ6j7AAuW5BNLcot/aLQUl+9LDWQMAAAAAAAAAAICWmAAAAAAAXK7sxW/LwQLDsGA03vtrPACOcgpfjSEDAAAAAAAAAACAlpgAAAAAAJKlLWgT/tACtcmpnRAf61zHQa/Og7BIAwAAAAAAAAAAgJaYAAAAAADqDQRlDpfSAuBuZC+SUjpWA++gEMrgAQMAAAAAAAAAAICWmAAAAAAAnkU3srLw0wKMXcaD5qvkgg0UYMdwkVsDAAAAAAAAAAAALTEBAAAAAJAZHjHtUtQCCKLnnR3xd4g8RgOMgsgQAwAAAAAAAAAAgNHwCAAAAADoLTTWN9rVAqosNZVHqmBLEECOTOeHYwMAAAAAAAAAAIDDyQEAAAAAMu2Kdx8M2AKp9WvCrWav0p6tw+saoR0DAAAAAAAAAACAlpgAAAAAANhohzZdFNoCCKLnnR3xd4hOn5fsaHVcAwAAAAAAAAAAAFpiAgAAAADYaIc2XRTaAtSy2IvTBZ4I+m2ubE1cfwMAAAAAAAAAAICWmAAAAAAAjvb1TjUC3QJwC8TK+F/Z0DCoMtc4XhUDAAAAAAAAAACAlpgAAAAAAJnRgvpwX+ECvFGFYUobwMtrd4PlELcLAwAAAAAAAAAAgJaYAAAAAADsZhjlmDnrAmtE6nlU7pgABG4fwU3QawMAAAAAAAAAAABaYgIAAAAA3f/wqYPc9gJqIJ43Admxy8DuYd5qrlEDAAAAAAAAAACAlpgAAAAAAN3/8KmD3PYCjw4KfGrLB/m81yscSclFA4CWmAAAAAAAgJaYAAAAAADd//Cpg9z2AtbDwtDtLlAq367HF2TdKwOAlpgAAAAAAICWmAAAAAAA3f/wqYPc9gJsph3PwHeZea62z8d3VHADgMPJAQAAAACAw8kBAAAAAN3/8KmD3PYCEPyQzA62NDt4aC11CbAqA4CWmAAAAAAAgJaYAAAAAADd//Cpg9z2AvuNl6PuA6U52sop+qWuYAOAw8kBAAAAAIDDyQEAAAAA3f/wqYPc9gKKXI++6cx0erUAYCI6f3MDgHeOBgAAAACAd44GAAAAAN3/8KmD3PYC56WJQLTiWUDJJjWk831FA4CWmAAAAAAAgJaYAAAAAAD8JsUmlJ75AlyUQjcdXcp/iqWzrebLRwMAAAAAAAAAAICWmAAAAAAAx8Ww3rl6/AKaThdLNhVXI1gWqcKSAycDAAAAAAAAAAAALTEBAAAAAMfFsN65evwCu3n+xfB6SIzogA/B0uw3A4CWmAAAAAAAgJaYAAAAAAAD76AQyuABA+Hey7tJYAjia+cQWGGk8AMAAAAAAAAAAICWmAAAAAAAb/8zQXSDBQPznPwnLpKPDrEItl/KR8ADgJaYAAAAAACAlpgAAAAAAG//M0F0gwUDdWeYuFSqNqPZ30ilUmCgA4AdLAQAAAAAgB0sBAAAAABrd4PlELcLA2OmoekowFQ6sB9kFl6nkQMAAAAAAAAAAICWmAAAAAAAPEYDjILIEAPimolEtlPd8LyoamEBgt8DAAAAAAAAAACA0fAIAAAAACGRfg/OIhED2lXP3hMDr7gGajN6aHGiAwAAAAAAAAAAgJaYAAAAAAAwqDLXOF4VA7EnCRb3aswWKx8jIRqW1gMAAAAAAAAAAICWmAAAAAAAlb24Z0jWGAOfRw8beoFjary/1zUXHZ4DAAAAAAAAAAAAh5MDAAAAAGb4NbCLZxkDXjPFr7CdLJ2diBzSV7LvAwAAAAAAAAAAgJaYAAAAAAAdbad7ScAcA8VOi6ZAhQey8zST/zoKgQMAAAAAAAAAAAAtMQEAAAAAqpkPx+QiHQM4emcnFFCj1dxqb7fB5vsDAAAAAAAAAACAlpgAAAAAAJ6tw+saoR0DZfFhRnq9Giy840wJ6l76A4CWmAAAAAAAgJaYAAAAAABfnVhCg3MeA//PkK5tpfccIAsaILFR8QMALTEBAAAAAIDDyQEAAAAAEE93KO8xHwPwsS0i19VqayfVZmQ5YbMDAAAAAAAAAACAw8kBAAAAAACOcgpfjSED5Tswpf2GKAbdpSt6G02RAwAAAAAAAAAAgJaYAAAAAAAPhQTHNTgiAz34B2/OU+iOcBL7/V/s/gOAlpgAAAAAAICWmAAAAAAAm/GdHK42JAN1Z5i4VKo2o5x8jwJPXsoDgJaYAAAAAACAlpgAAAAAAJvxnRyuNiQD85z8Jy6Sjw4GBmFvyAqSA4CWmAAAAAAAgJaYAAAAAAAHCvWdExomA9pVz94TA6+4sASbeG6yzQMAAAAAAAAAAICWmAAAAAAAWBapwpIDJwPQtZgxgpXYNMqdNkhYF/QDAAAAAAAAAAAALTEBAAAAANQxtcJBFy4D2MmrOsxrigqU0yMftBDyAwAAAAAAAAAAAFpiAgAAAACH3/ccLck0A6n1a8KtZq/SROxHiLfogwMAAAAAAAAAAICWmAAAAAAAWVT86+AFOgNDwB+ZM/zrHwXTpBLrYOADgJaYAAAAAACAlpgAAAAAAKSX7/kPy0YDjF3Gg+ar5IK62U0N1h3GAwAAAAAAAAAAAC0xAQAAAACKpbOt5stHA4k7NaM/BeL0bDtvmbmS5QMAAAAAAAAAAICWmAAAAAAAx0GvzoOwSAPJerpVZipgQ5qG1fFKAIYDAAAAAAAAAACAlpgAAAAAABRejamHkU4DXUBE4epg5gt3Wne+9FnLAwAAAAAAAAAAgJaYAAAAAADA7mHeaq5RA7UWby3gY8wzWlLeJSgmqAOAlpgAAAAAAICWmAAAAAAALQUl+9LDWQMjd7ETu2H1phlJe4X9x4kDgJaYAAAAAACAlpgAAAAAAA0UYMdwkVsDrknZj2VgxBeI969Rj+eRAwAAAAAAAAAAAC0xAQAAAABOn5fsaHVcA+KaiUS2U93wN6sQKJpr3QMAAAAAAAAAAABaYgIAAAAAN2cBGQDrXQPb5b7PcLzaJUhFuQC24pIDAAAAAAAAAACAlpgAAAAAAENf+htjqGIDpOuvxc915m1HFtoSiYaUAwAAAAAAAAAAgJaYAAAAAAAQQI5M54djAyjjYW55iNufweBmo03S6gMAAAAAAAAAAICWmAAAAAAAEECOTOeHYwOUpO+DFkto5CXh57LFTKoDAAAAAAAAAACAlpgAAAAAABBAjkznh2MDdDK8G0IoAAnsHxdA2GCBA4CWmAAAAAAAgJaYAAAAAAAEbh/BTdBrA6ksmAXwqTabcfcixL5e/QMAWmICAAAAAABaYgIAAAAAGnPI8JahcgNHEuXgpXsJcp9Kttu/X+QDgJaYAAAAAAAALTEBAAAAAPptrmxNXH8D/X+kiP3agLHHeNPFloL8AwAAAAAAAAAAgJaYAAAAAADzNJP/OgqBA1W3xIUQn91D7+QWUqW0ZAQAAAAAAAAAAAAtMQEAAAAAROxHiLfogwPK1qreFSyTU9O7wnonVgkEAAAAAAAAAACAlpgAAAAAAJqG1fFKAIYDyERC63z7VluCoIuxuQ9XBAAAAAAAAAAAgJaYAAAAAADdpSt6G02RA2tnVBDGtkGnc/8Q0dwkFASAlpgAAAAAAICWmAAAAAAAsB9kFl6nkQPu5d7bL0QqHTlWh3zQ4lIEAAAAAAAAAACAlpgAAAAAAIj3r1GP55EDHRfnQjOS+jOl7cN644Y/BAAAAAAAAAAAAC0xAQAAAABIRbkAtuKSAx2hVTs1RMDIfHIQJOjKWAQAAAAAAAAAAICWmAAAAAAARxbaEomGlAMSembQSV8v0esBnKpsWgMEAAAAAAAAAACAlpgAAAAAALy/1zUXHZ4D+vtcB7NctRDwe7ln49poBAAAAAAAAAAAgJaYAAAAAAC8v9c1Fx2eA9WnoFkEPj24Iv0fVck7ewSAlpgAAAAAAICWmAAAAAAAvL/XNRcdngMDfDTWwFBdPpst3wEgexwEAAAAAAAAAAAAWmICAAAAAAZqM3pocaID4UrRXm6NbW9nzuslxr9ABAAAAAAAAAAAgJaYAAAAAAAl4eeyxUyqA8rvkCcMcHfw3PL/TOEyCgQAAAAAAAAAAICWmAAAAAAAJ9VmZDlhswMjMSKvzg3v2c0Wfd8X9C0EAAAAAAAAAACAw8kBAAAAALrZTQ3WHcYDrknZj2VgxBcnt+orpo1QBAAAAAAAAAAAAC0xAQAAAAB3Wne+9FnLA0qUKUqov2wwKOf5BeT9GAQAAAAAAAAAAICWmAAAAAAAsASbeG6yzQPhStFebo1tb8ZvnJqkwWkEAAAAAAAAAACAlpgAAAAAACsfIyEaltYDOHpnJxRQo9U6PuT1iAk6BAAAAAAAAAAAgJaYAAAAAAA3qxAommvdA0ViIFU4Jg24IVDFJiyfIAQAAAAAAAAAAABaYgIAAAAAvKhqYQGC3wOxmBYxGXTUcKPFhIct7GEEAAAAAAAAAACA0fAIAAAAAJ9Kttu/X+QDCcpU1BeZn8KrrtBk4NolBAAAAAAAAAAAgJaYAAAAAABsO2+ZuZLlA2tR4D8YRs0UMvQrrGnCOQQAAAAAAAAAAICWmAAAAAAAweBmo03S6gNzbPhDzBobL2zD5f8t+XoEAAAAAAAAAACAlpgAAAAAAJ2IHNJXsu8DQ3jD0dDkbzEcwyDAlIcjBICWmAAAAAAAgJaYAAAAAABr5xBYYaTwAwYxFat6VkIItx1sEf1bWAQAAAAAAAAAAICWmAAAAAAAIAsaILFR8QP7jZej7gOlOW0NORTxSyAEgJaYAAAAAACAlpgAAAAAAJTTIx+0EPIDumK3BsgN4fwM/ppy7LsCBABaYgIAAAAAAFpiAgAAAADKnTZIWBf0A9jJqzrMa4oKUwp9B80SXwQAAAAAAAAAAAAtMQEAAAAA3Gpvt8Hm+wO6YrcGyA3h/J2R1ngQd1UEgJaYAAAAAACAlpgAAAAAAMd408WWgvwDhWGUY0ETB+uVPVd8nFd7BAAAAAAAAAAAgJaYAAAAAADrAZyqbFoDBP/PkK5tpfccgCxMHSQr9gQAAAAAAAAAAICWmAAAAAAA07vCeidWCQReM8WvsJ0snYj8q/0Dpu8EAAAAAAAAAACAlpgAAAAAANzy/0zhMgoE+I01jwBWMyyW2vBQZZ61BAAAAAAAAAAAgJaYAAAAAAAo5/kF5P0YBDnWw6fOTmtEoDDCqJltnwQAAAAAAAAAAICWmAAAAAAAmy3fASB7HAR7RRlDltQKosT75EB4yfUEAAAAAAAAAACAw8kBAAAAAJst3wEgexwEmSM2I+8EZcZuIcEHYxjaBAAAAAAAAAAAgJaYAAAAAAAhUMUmLJ8gBBOJoiOWDSqRMHx7bJhI/AQAAAAAAAAAAABaYgIAAAAAq67QZODaJQTYer09rKL6gtpfKLLdR7YEAAAAAAAAAACAlpgAAAAAAM0Wfd8X9C0EryQJIXXRJvV9q2b9OdnaBAAAAAAAAAAAgMPJAQAAAAAy9CusacI5BEmhqZnHx1tx7Ps8Q+h+jwQAAAAAAAAAAICWmAAAAAAAOj7k9YgJOgS6YrcGyA3h/Fz2kULsrdEEgJaYAAAAAACAlpgAAAAAAKXtw3rjhj8EBkeHDiOEL+csJ/DvybGLBAAAAAAAAAAAAC0xAQAAAABnzuslxr9ABEREGx7ntrLy06YVH4EcvQSAlpgAAAAAAICWmAAAAAAAJ7fqK6aNUAQdF+dCM5L6M7gKT4aRbfYEAAAAAAAAAAAALTEBAAAAADlWh3zQ4lIEupFG1qZepzGZWOMfA9uABAAAAAAAAAAAgJaYAAAAAACCoIuxuQ9XBMtXqaoQaVrtGLR73kHCsQQAAAAAAAAAAICWmAAAAAAAtx1sEf1bWATbAlKiOJakxImzbTFXC94EAAAAAAAAAACAlpgAAAAAAHxyECToylgEmizhzXvTGGxDqYWL7KefBAAAAAAAAAAAgJaYAAAAAABTCn0HzRJfBLpitwbIDeH8zWc/HN98vgQALTEBAAAAAAAtMQEAAAAAo8WEhy3sYQQsTzgSyRh/uW2uCgWZydEEAAAAAAAAAACA0fAIAAAAAO/kFlKltGQETwewdx57jM9izaODgZTMBAAAAAAAAAAAgJaYAAAAAADv5BZSpbRkBMVOi6ZAhQeyhO1kjkFJ0gSAlpgAAAAAAICWmAAAAAAA8Hu5Z+PaaATbhSynZfboXRDKksqFDNwEAAAAAAAAAACAlpgAAAAAAMZvnJqkwWkEREQbHue2svIj28/CptmmBICWmAAAAAAAgJaYAAAAAABsw+X/Lfl6BIbhCcQ0wn5fFpP6FQWOrwQAAAAAAAAAAICWmAAAAAAAlT1XfJxXewTCgYVSwd9GxoMzxX/iN6cEAAAAAAAAAACAlpgAAAAAAJlY4x8D24AEclBMVLJGkT87AKvjhV0qBQAAAAAAAAAAgJaYAAAAAAAsJ/DvybGLBKksmAXwqTabU5Y/u7SXTgUALTEBAAAAAAAtMQEAAAAA7Ps8Q+h+jwRM/u9O9sACUslZeOZnlhEFAAAAAAAAAACAlpgAAAAAAKAwwqiZbZ8EzeOe990RHUoFcOMe7jAtBQAAAAAAAAAAgJaYAAAAAABDqYWL7KefBO7HASIibT0qAQOhzn0YKAUAAAAAAAAAAICWmAAAAAAAgzPFf+I3pwS3yMSNNhIZ/IuJXNDxiBcFAAAAAAAAAACAlpgAAAAAABaT+hUFjq8EqfVrwq1mr9LPlXNF3KkoBQAAAAAAAAAAgJaYAAAAAAAYtHveQcKxBAYmn4SxwWKvnVDBcAdAXgUAAAAAAAAAAICWmAAAAAAAltrwUGWetQSpPgwtZQcAN6odrMjgcGAFAAAAAAAAAACAlpgAAAAAANpfKLLdR7YE5CgoAah7SHxbcCmlDWkxBQAAAAAAAAAAgJaYAAAAAABizaODgZTMBIGUQD7gZPVLudaCIA1oZgUAAAAAAAAAAICWmAAAAAAAba4KBZnJ0QRQvbnXthIpbow/Z+cDS0YFAAAAAAAAAAAAh5MDAAAAAG2uCgWZydEE5lst2nIIqXBt8dozhMtGBQAAAAAAAAAAgEpdBQAAAABuIcEHYxjaBOhEKSTHoql02AQSQVEvcgUAAAAAAAAAAICWmAAAAAAAfatm/TnZ2gRLMm6dD1SGnV+f1zn7bD0FAAAAAAAAAACAlpgAAAAAAH2rZv052doEVotoGob81npjbRLjP5pJBQAAAAAAAAAAAC0xAQAAAAAQypLKhQzcBGPoFr12xtVp8kzqRNs+fQUAAAAAAAAAAICWmAAAAAAAibNtMVcL3gTVIROTH7MTnsK58MZs4jIFgJaYAAAAAACAlpgAAAAAAIj8q/0Dpu8EqT4MLWUHADd6xgpay2haBQAAAAAAAAAAgJaYAAAAAADE++RAeMn1BGq92TUkv4R8kgWbFZzhNwUAAAAAAAAAAICWmAAAAAAAxPvkQHjJ9QTKm8K1AliYVGxKKcDxOioFAAAAAAAAAACAlpgAAAAAAMT75EB4yfUE2SMJe5MdJQPd+wV6AsonBQAAAAAAAAAAgJaYAAAAAACALEwdJCv2BHcJIDA8X0wOLK6D6YSITgUAAAAAAAAAAICWmAAAAAAAuApPhpFt9gQGR4cOI4Qv55K6SzH1PVYFAAAAAAAAAAAALTEBAAAAADB8e2yYSPwEAet/rBPK1hcHOCE6pe8lBQAAAAAAAAAAAFpiAgAAAADJWXjmZ5YRBbEnCRb3aswWonbaI3+7jwUAAAAAAAAAAICWmAAAAAAAi4lc0PGIFwVIiKAw04/Megj3wtI35f8FAAAAAAAAAACAlpgAAAAAAAc4ITql7yUFBA1lTThBrFl9Q9h5GE6GBQAAAAAAAAAAgJaYAAAAAAAHOCE6pe8lBXH+xbv30EVRJqTz7Fz5zAUAAAAAAAAAAAAtMQEAAAAABzghOqXvJQVrF1/PDZl5PxwW8PxHHbgFAAAAAAAAAACAlpgAAAAAAN37BXoCyicFN9sAa9PrU1BBAv9fsHeqBYCWmAAAAAAAgJaYAAAAAAABA6HOfRgoBaksmAXwqTabEO699KVE3QWAlpgAAAAAAICWmAAAAAAAz5VzRdypKAXK1qreFSyTU3r2GfeY4IsFAAAAAAAAAACAlpgAAAAAAGxKKcDxOioFGlzckJDbhs3y89ZXawGgBYCWmAAAAAAAgJaYAAAAAAA7AKvjhV0qBfti8kBbKdiFcgKiBdmC5gUAAAAAAAAAAICWmAAAAAAABXDjHu4wLQUmuGwr0jjZ6zrRsoKAeNsFAAAAAAAAAACAlpgAAAAAAFtwKaUNaTEFXGZemHua7WJnnoQZtU6xBYCWmAAAAAAAgJaYAAAAAACSBZsVnOE3BUwSMB3OhPLIOsSj6u+16gUAAAAAAAAAAICWmAAAAAAAX5/XOftsPQXh7iMrd8e//HOt1UQNLNEFgJaYAAAAAACAlpgAAAAAAIw/Z+cDS0YFfLkgt7XukjYti+AlrvCABQAAAAAAAAAAAIeTAwAAAABt8dozhMtGBQGTAk22VXGeqNvgKZLq6gUAAAAAAAAAAAAtMQEAAAAAbfHaM4TLRgW7DbRiOa8JkidhUkNjWpAFAAAAAAAAAAAAWmICAAAAAG3x2jOEy0YFGV7NEcF71aNkcc0f4qvGBQAAAAAAAAAAAC0xAQAAAABt8dozhMtGBcSVmuZ5gmc6XcSvw3Hf3wUAAAAAAAAAAICWmAAAAAAAY20S4z+aSQWG4QnENMJ+X1Rtkk3SNf0FAAAAAAAAAACAlpgAAAAAAGNtEuM/mkkFwZmhCB37Dg00PxpZj6uABQAAAAAAAAAAgJaYAAAAAAAsroPphIhOBXVnmLhUqjajyBy/IIhW9QWAlpgAAAAAAICWmAAAAAAAkrpLMfU9VgWpLJgF8Kk2m7za5p2kgJ0FAC0xAQAAAAAALTEBAAAAAHrGClrLaFoFa1HgPxhGzRTDS/u0nmjABQAAAAAAAAAAgJaYAAAAAACdUMFwB0BeBYMOWUDY/o00SdRe7uMbhgUAAAAAAAAAAICWmAAAAAAAqh2syOBwYAVrUeA/GEbNFGQvicMaS90FAAAAAAAAAACAlpgAAAAAALnWgiANaGYF8RZXb3ZaciK4QFX/PgDHBQAAAAAAAAAAgJaYAAAAAADYBBJBUS9yBShvFTOvVFwRxd8UT2XAjAUAAAAAAAAAAICWmAAAAAAA8kzqRNs+fQVWpta3gWGu5Sn6rXZe+tEFAAAAAAAAAACAlpgAAAAAADQ/GlmPq4AFGF2inv8oC/gfDADX6iRaBoCWmAAAAAAAgJaYAAAAAAAti+AlrvCABRK5UrhZh7COKtIoL53EcAYAAAAAAAAAAICWmAAAAAAALYvgJa7wgAWN2MfAyI0MInv9owe7fToGAAAAAAAAAACA8PoCAAAAAEnUXu7jG4YFoVmChXFbeMqDE7Zn4PshBoCWmAAAAAAAgJaYAAAAAAB9Q9h5GE6GBdBbN4J9cKZLQWQigzR6MAYAAAAAAAAAAICWmAAAAAAAevYZ95jgiwVeM8WvsJ0snTGn4XdDfBMGAAAAAAAAAACAlpgAAAAAAMXfFE9lwIwFwwP+Zw0ex1+xRJYGDQhZBoCWmAAAAAAAgJaYAAAAAACidtojf7uPBTh6ZycUUKPVEcQfq7/geQYAAAAAAAAAAICWmAAAAAAAJ2FSQ2NakAV4RA+910vDWOvHvZ/CYmsGAAAAAAAAAAAAWmICAAAAABwW8PxHHbgF8t/eAjsXvuX8dXO11vwSBgAAAAAAAAAAgJaYAAAAAADDS/u0nmjABTlR3gBU0mvIL86zXc0DLwYAAAAAAAAAAICWmAAAAAAAZHHNH+KrxgVRws6TIrggXPuNVN17dAMGAAAAAAAAAACAlpgAAAAAAGRxzR/iq8YFO7ZLI1svhzby+kGaYthGBgAAAAAAAAAAgJaYAAAAAAC4QFX/PgDHBQOCI3zGyrIwbPPl8W8xcAaAlpgAAAAAAICWmAAAAAAAJqTz7Fz5zAXy394COxe+5dUGAFaaFj0GAAAAAAAAAACAlpgAAAAAACak8+xc+cwFYe/KEDsyGXiZAaj4wk1FBgAAAAAAAAAAgJaYAAAAAAAp+q12XvrRBbr3NVRFhyYYMs7aXbNZMQaAlpgAAAAAAICWmAAAAAAAOtGygoB42wVFKblWEEOtILqGlztLVjEGAAAAAAAAAACAlpgAAAAAAGQvicMaS90FOVHeAFTSa8jRf2kwGzxEBgAAAAAAAAAAgJaYAAAAAABdxK/Dcd/fBUqUKUqov2wwZjzqwZ0jbgYAAAAAAAAAAICWmAAAAAAAcgKiBdmC5gX4fblWbXAQeyjk4FxFUV0GAAAAAAAAAACAlpgAAAAAADrEo+rvteoFFefpy8r1y0htLk9AxnN1BoCWmAAAAAAAgJaYAAAAAACo2+ApkurqBcUegS5iNcwmj49C5gbgIgYAAAAAAAAAAAAtMQEAAAAAVG2STdI1/QWp9WvCrWav0uAzj6Bx3wgGAAAAAAAAAACAlpgAAAAAAAj3wtI35f8F4rNJD/rosN2c6KG4mpgoBgAAAAAAAAAAgJaYAAAAAAD7jVTde3QDBlCuScdKML1KHs5ImJyIyAYAAAAAAAAAAICWmAAAAAAA4DOPoHHfCAYh2fh4bosDjUs3db1lXNUGAAAAAAAAAACAlpgAAAAAAPx1c7XW/BIGqfVrwq1mr9LSf2H0B2ivBoCWmAAAAAAAgJaYAAAAAAAxp+F3Q3wTBkN4w9HQ5G8xlj/C3NjkvgaAlpgAAAAAAICWmAAAAAAAj49C5gbgIgYZrLVWkon4tvI8Km9jdJEGAAAAAAAAAAAALTEBAAAAAJzoobiamCgGI9NTkMyWW7d0EuPAQr6yBgAAAAAAAAAAgJaYAAAAAAAvzrNdzQMvBuvyaHNTRVcgn4Y53wXshAYAAAAAAAAAAICWmAAAAAAAQWQigzR6MAYZPIbLE+dNxKq8jhvdPKUGAAAAAAAAAACAlpgAAAAAALqGlztLVjEGe4Th0bdHyqic/crW+o3LBgAAAAAAAAAAgJaYAAAAAAB7/aMHu306Bib3WRNj/VQOgQomYtnilQYAAAAAAAAAAIDw+gIAAAAA1QYAVpoWPQap9WvCrWav0gyzjy5NI9IGAAAAAAAAAACAlpgAAAAAANF/aTAbPEQG6/Joc1NFVyA5lrHCYgSPBgAAAAAAAAAAgJaYAAAAAACZAaj4wk1FBoLzzCq5bhGqg4juIsWE0gYAAAAAAAAAAICWmAAAAAAA8vpBmmLYRgaRY2O3J4YCacs31CuUQMUGAAAAAAAAAACAlpgAAAAAACjk4FxFUV0GS1cekAHePHo1Bu+wR/u5BgAAAAAAAAAAgJaYAAAAAADrx72fwmJrBjOeTtT1wCTJp4M6ZUJEkwYAAAAAAAAAAABaYgIAAAAAZjzqwZ0jbgY51sOnzk5rRG1qpGJJ2rQGgJaYAAAAAACAlpgAAAAAACrSKC+dxHAGfmAFANfFpdI9O3ZjN8fRBgAAAAAAAAAAgJaYAAAAAAARxB+rv+B5BrpitwbIDeH8Zzudrksc/gaAlpgAAAAAAICWmAAAAAAAn4Y53wXshAY7Qg6tSD4gNwUuQl+UKWkHAAAAAAAAAACAlpgAAAAAADmWscJiBI8GIHOCNYhPr2BShaol+X8QBwAAAAAAAAAAgJaYAAAAAADyPCpvY3SRBna9OyQiw6yqhojNj+KRTAcAAAAAAAAAAAAtMQEAAAAAp4M6ZUJEkwZz1MV0tStPamAwsfpEHHoHAAAAAAAAAAAALTEBAAAAAKeDOmVCRJMG0+XufhUB6MSQR43rMw5qB4CWmAAAAAAAgJaYAAAAAACngzplQkSTBg/EJYLTmdXHdDTKp8x9fAcAAAAAAAAAAICWmAAAAAAAgQomYtnilQZqz1tjXN1MPdMsVKwAZEUHAAAAAAAAAAAALTEBAAAAAIEKJmLZ4pUGlwxfZlJq98DU+T/O/r1+BwAAAAAAAAAAgMPJAQAAAACqvI4b3TylBvGUZOnCfnNKJ/z+UTQregcAAAAAAAAAAICWmAAAAAAAdBLjwEK+sgbJerpVZipgQyEhMNEmODsHAAAAAAAAAACAlpgAAAAAADUG77BH+7kGuMr+QH7Zt595yg+nxuZSBwAAAAAAAAAAgJaYAAAAAADLN9QrlEDFBs6m/PsaRIKLUWu41MNuXQcAAAAAAAAAAICWmAAAAAAAHs5ImJyIyAbVIROTH7MTnuBvimScvzgHgJaYAAAAAACAlpgAAAAAAJz9ytb6jcsG9BVzNOPRxEzPzUP2LzAKBwAAAAAAAAAAgJaYAAAAAAA9O3ZjN8fRBpcMX2ZSavfAYlnv4ngZcQcAAAAAAAAAAICWmAAAAAAADLOPLk0j0gbkBXxwXoGTi3ERPERbnWcHgJaYAAAAAACAlpgAAAAAAIOI7iLFhNIGHx5GrInl0V9hXxOHKe0ZBwAAAAAAAAAAgJaYAAAAAABLN3W9ZVzVBtEN7qUbgg5zU6e23rIUEAcAAAAAAAAAAICWmAAAAAAAz81D9i8wCgcyr4Hs136ElrGbBfOa04cHAAAAAAAAAACAlpgAAAAAAFOntt6yFBAHpJaHS0ImTimjXlRoDni/BwAAAAAAAAAAgJaYAAAAAABShaol+X8QB0wN+I2qrTS/QWxQF+Av9weAlpgAAAAAAICWmAAAAAAAYV8ThyntGQfP5mRbZFv0X5Y9X17+9eAHgJaYAAAAAACAlpgAAAAAACEhMNEmODsHyERC63z7VlusyBIx7dXmBwAAAAAAAAAAgJaYAAAAAADTLFSsAGRFB1Wsb1IeyqqFv9Fl1LaClQcAAAAAAAAAAAAtMQEAAAAAhojNj+KRTAdG3wEYthNxTczysZa3J9UHAAAAAAAAAAAALTEBAAAAAHnKD6fG5lIH2EtV7j/ZrJ4Lq7kqx4CTBwAAAAAAAAAAgJaYAAAAAABRa7jUw25dB6bGC4nJ7PpEIrxx0JddigcAAAAAAAAAAICWmAAAAAAABS5CX5QpaQfJJmcJbvve9YfUxeFJzoIHAAAAAAAAAACAlpgAAAAAAGJZ7+J4GXEH8beyuRhUkCmDu7622+GDBwAAAAAAAAAAgJaYAAAAAABgMLH6RBx6B1VdBBt17qJnyQMDFRHW/AcAAAAAAAAAAAAtMQEAAAAAJ/z+UTQregctib/4Kmh83/ynNYFgwpoHAAAAAAAAAACAlpgAAAAAAHQ0yqfMfXwH2e7iqLTstkHk0HptZNbjBwAAAAAAAAAAgJaYAAAAAADU+T/O/r1+B/G3srkYVJApO9RC29rv+AcAAAAAAAAAAIDDyQEAAAAAh9TF4UnOggdt5P9QJ7P0gWhehl9RuiYIgJaYAAAAAACAlpgAAAAAAIO7vrbb4YMHXaVARkiI/d5dWVj+kCtZCAAAAAAAAAAAgJaYAAAAAACxmwXzmtOHB8JAbvbcb1HqR3I0HTg8PwiAlpgAAAAAAICWmAAAAAAAIrxx0Jddigep9WvCrWav0sOB6GTGSXIIAAAAAAAAAACAlpgAAAAAAAuruSrHgJMHBkeHDiOEL+chX9O2MlpxCAAAAAAAAAAAgJaYAAAAAAC/0WXUtoKVB2xk++5fxgzjt81IGYK9SgiAlpgAAAAAAAAtMQEAAAAA/Kc1gWDCmgda6sJ3kzMCPMdSywgrPngIAAAAAAAAAACAlpgAAAAAAKNeVGgOeL8HMKyRhxrpxyTv5jVXfjAjCICWmAAAAAAAgJaYAAAAAADM8rGWtyfVB8g6m4BlxqYBg+pHGkx+fAgAAAAAAAAAAAAtMQEAAAAA5NB6bWTW4wcMIJIkJ+C6MKfckdxL1UgIAAAAAAAAAACAlpgAAAAAAKzIEjHt1eYHy1epqhBpWu3dgIjZ/EM4CAAAAAAAAAAAgJaYAAAAAAA71ELb2u/4B12lQEZIiP3eIATZXUjHLwgAAAAAAAAAAIDDyQEAAAAAyQMDFRHW/AeIL4qJQeItszCMlQzQFH8IAAAAAAAAAAAALTEBAAAAACAE2V1Ixy8IXSRreh31Aibuon5Nh8bFCAAAAAAAAAAAgMPJAQAAAADdgIjZ/EM4CAYmn4SxwWKvoKav3qxw9ggAAAAAAAAAAICWmAAAAAAAp9yR3EvVSAgzmGHnJbPurGTIJSc6puoIAAAAAAAAAACAlpgAAAAAALfNSBmCvUoIm6H9X6I4SFug7VJ2c+iqCICWmAAAAAAAgJaYAAAAAABdWVj+kCtZCF0ka3od9QIm5ZDcFd7T+ggAAAAAAAAAAICWmAAAAAAAIV/TtjJacQipLJgF8Kk2m9ML3Ay8zYYIgJaYAAAAAACAlpgAAAAAAMOB6GTGSXII3UYgkVNtBmV7zLTXvGaICICWmAAAAAAAgJaYAAAAAADHUssIKz54CFrqwneTMwI8XFK2+DpIqwgAAAAAAAAAAICWmAAAAAAAg+pHGkx+fAjZ8UHwc2oWL2Zxb+ucbooIAAAAAAAAAAAALTEBAAAAADCMlQzQFH8IlM8DDVzAGh3iF0wma5nGCAAAAAAAAAAAAC0xAQAAAABmcW/rnG6KCIxdxoPmq+SCc6EScL/ndwkAAAAAAAAAAAAtMQEAAAAAXFK2+DpIqwh9SSEXICR1eN07u8RpChIJAAAAAAAAAACAlpgAAAAAAO6ifk2HxsUITatWzfpiEzelrPjtb1tZCQAAAAAAAAAAAC0xAQAAAADuon5Nh8bFCKvUfrv8wCyII7lIt24ZPAmAlpgAAAAAAICWmAAAAAAA4hdMJmuZxggDTsUgBcJ+Ak9N8qJJBUsJAAAAAAAAAAAALTEBAAAAAGTIJSc6puoI+tAy72OfWHNUpm0NY5gDCQAAAAAAAAAAgJaYAAAAAACgpq/erHD2CIMOWUDY/o00WwYUfpcTTQkAAAAAAAAAAICWmAAAAAAA5ZDcFd7T+gi2xqYIqOnAXvE3MzEVVyoJAAAAAAAAAACAlpgAAAAAAFSmbQ1jmAMJjxk8xF6hp9XwzVXt3rz2CQAAAAAAAAAAgJaYAAAAAADdO7vEaQoSCRk8hssT503EqXnqUzhv3QkAAAAAAAAAAICWmAAAAAAA8TczMRVXKglCm86ucQLlUiWSQmZrFvQJAAAAAAAAAACAlpgAAAAAAE9N8qJJBUsJJjikSTWt7fH0X7EuTLmECQAAAAAAAAAAAC0xAQAAAABbBhR+lxNNCaFZgoVxW3jKPVhUBYPHggmAlpgAAAAAAICWmAAAAAAApaz47W9bWQlCm86ucQLlUsWHKzN8eN4JAAAAAAAAAAAALTEBAAAAAHOhEnC/53cJrknZj2VgxBeel+Pt23GdCQAAAAAAAAAAAC0xAQAAAAD0X7EuTLmECabGC4nJ7PpE6sN3no0UYAoAAAAAAAAAAICWmAAAAAAA9F+xLky5hAmG4QnENMJ+X1PrOBiqpDAKAAAAAAAAAACAlpgAAAAAAJ6X4+3bcZ0JHRfnQjOS+jOEOZVPoYRUCgAAAAAAAAAAAC0xAQAAAACpeepTOG/dCfGUZOnCfnNKLy4ll1uHJQoAAAAAAAAAAICWmAAAAAAAxYcrM3x43gnHlVgnObRT3OquolrWgVYKAAAAAAAAAACAlpgAAAAAAMWHKzN8eN4JoLv7kQTRG36UjGxwXBIKCgAAAAAAAAAAgJaYAAAAAAAlkkJmaxb0CceVWCc5tFPcL6jec2ZhFAoAAAAAAAAAAICWmAAAAAAA8M1V7d689glcfPTJKceytRpCDQbpXEIKAAAAAAAAAACAlpgAAAAAAJSMbHBcEgoKFRam6Bo/3PtMZm+yQhnqCgAAAAAAAAAAgJaYAAAAAAAvqN5zZmEUCoX9rFoUBnpb4PKChgEiwgqAlpgAAAAAAICWmAAAAAAALy4ll1uHJQotib/4Kmh83w0XH30fM6YKAAAAAAAAAACAlpgAAAAAAFPrOBiqpDAKqfVrwq1mr9JL3leFwI2yCgAAAAAAAAAAgJaYAAAAAAAaQg0G6VxCCmo+DuXA7FMg9WEQ4uL2rwoAAAAAAAAAAICWmAAAAAAAhDmVT6GEVAoGR4cOI4Qv52DGbflIgvEKAAAAAAAAAAAALTEBAAAAAOquolrWgVYKqfVrwq1mr9Len0bvyMmlCoCWmAAAAAAAgJaYAAAAAADqw3eejRRgCoX9rFoUBnpbCi5+OgcbggqAlpgAAAAAAICWmAAAAAAADRcffR8zpgpa6sJ3kzMCPGRP24EG2BcLAAAAAAAAAACAlpgAAAAAAPVhEOLi9q8KjT7PwNDsRlPosknRelQxCwAAAAAAAAAAgJaYAAAAAABL3leFwI2yCsrWqt4VLJNTE+slwuusIAsAAAAAAAAAAICWmAAAAAAATGZvskIZ6grCQG723G9R6iYwjwSNq0cLgJaYAAAAAACAlpgAAAAAAGDGbflIgvEKqSyYBfCpNps8Hgt7UvM1CwAtMQEAAAAAAC0xAQAAAABkT9uBBtgXC1rqwneTMwI8jzcd2K7LygsAAAAAAAAAAICWmAAAAAAAE+slwuusIAteM8WvsJ0snQagaBZ9YIkLAAAAAAAAAACAlpgAAAAAAOiySdF6VDELHRfnQjOS+jMhgVdNN0SNCwAAAAAAAAAAgJaYAAAAAAAGoGgWfWCJC6k+DC1lBwA3XI6O4sh1SAwAAAAAAAAAAICWmAAAAAAAIYFXTTdEjQuzO1wJckN8mVTR7rA+nD4MAAAAAAAAAACAlpgAAAAAAI83Hdiuy8oLfUkhFyAkdXhNKX5C5gdKDAAAAAAAAAAAgJaYAAAAAABU0e6wPpw+DCHsDLRqK45nrAjLUGUykgwAAAAAAAAAAICWmAAAAAAAXI6O4sh1SAxrUeA/GEbNFO6RRYAR8KQMAAAAAAAAAACAlpgAAAAAAE0pfkLmB0oM/WFAXB6oZFT6EBqDCm/PDICWmAAAAAAAgJaYAAAAAACsCMtQZTKSDGtR4D8YRs0U/s5nCVHPYw0AAAAAAAAAAICWmAAAAAAA7pFFgBHwpAw5Ud4AVNJryPIS2M+fT3QNAAAAAAAAAACAlpgAAAAAAP7OZwlRz2MNxHRGTMJvD5jXFQrgVFjADQAAAAAAAAAAgJaYAAAAAADyEtjPn090DevyaHNTRVcgKX6FGJhasA0AAAAAAAAAAICWmAAAAAAAKX6FGJhasA07Qg6tSD4gN/XCNAdOIlIOAAAAAAAAAACAlpgAAAAAANcVCuBUWMANsScJFvdqzBar0nxIeN1FDgAAAAAAAAAAgJaYAAAAAACr0nxIeN1FDjh6ZycUUKPVO00OX2cO6w4AAAAAAAAAAICWmAAAAAAA9cI0B04iUg7JJmcJbvve9b7QFVBiYc0OAAAAAAAAAACAlpgAAAAAAL7QFVBiYc0OPsWKFsLHGdDeuomrFk0nDwAAAAAAAAAAgJaYAAAAAAA7TQ5fZw7rDrpitwbIDeH8DICiEE/MGA+AlpgAAAAAAICWmAAAAAAA3rqJqxZNJw8uYLgk2hbJwS7McU7/54QPgJaYAAAAAACAlpgAAAAAAA=="; - let r_b64_req = "pwNzU/o7HXjC+xBydW50aW1lLnJlbGVhc2VtRWIgVTgmDbhLZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvZXhwb3J0ZXIvZXhwb3J0ZXJoZWxwZXIuKCpsb2dzUmVxdWVzdCkuRXhwb3J07uXe2y9EKh0bbmV0Ligqc3lzRGlhbGVyKS5kaWFsU2luZ2xl0LWYMYKV2DQRcnVudGltZS5ub3Rlc2xlZXAPxCWC05nVxzRnaXRodWIuY29tL0NsaWNrSG91c2UvY2gtZ28vcHJvdG8uKCpSZWFkZXIpLnJlYWRGdWxs93LT9qoI8+g8Z2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvc2NyYXBlLigqc2NyYXBlTG9vcCkucmVwb3J0UiU9oObHTsIQcnVudGltZS5tZW1oYXNoOBBHhHCBcFPQNWdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28uKCpTZXNzaW9uKS51cGxvYWREYXRhY4/Ue2TCY+0acmVnZXhwLigqUmVnZXhwKS5kb0V4ZWN1dGXYenh/0ylv1QpydW50aW1lLkdDTFBlHdhZm+4hbmV0L2h0dHAuKCpwZXJzaXN0Q29ubikucm91bmRUcmlwBiafhLHBYq8fcnVudGltZS4oKnN0a2ZyYW1lKS5nZXRTdGFja01hcNYZrVAM3AhTRmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9wcm90by4oKkJsb2NrKS5FbmNvZGVDb2x1bW41mAMnyc2GEhlydW50aW1lLigqbWhlYXApLmluaXRTcGFus56VfBe9kkAccnVudGltZS4oKm1jYWNoZSkucmVsZWFzZUFsbCHZ+HhuiwONGnJ1bnRpbWUuKCptY2FjaGUpLm5leHRGcmVlOKhH8IQR9QBLZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci5idWlsZExhYmVsU2V0LmZ1bmMxIHOCNYhPr2ApcnVudGltZS4oKmlubGluZVVud2luZGVyKS5yZXNvbHZlSW50ZXJuYWzCQG723G9R6g9ydW50aW1lLm1lbW1vdmWUY2HJlul4WElnaXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9mbGF0ZS4oKmh1ZmZtYW5CaXRXcml0ZXIpLndyaXRlQmxvY2tEeW5hbWljz1UgT/pt6GJLZ2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvc2NyYXBlLigqc2NyYXBlTG9vcCkuc2NyYXBlQW5kUmVwb3J0LmZ1bmMyM5hh5yWz7qwwZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NoLWdvL3Byb3RvLigqUmVhZGVyKS5SZWFkF4eUveR8YncLc3RyaW5ncy5DdXSlYkFtlSd0BA1ydW50aW1lLndha2Vw2Z+qnMnEx2ERcnVudGltZS5jaGFuc2VuZDGUt/f6/nLT0Q1ydW50aW1lLm1jYWxs+42Xo+4DpTkOcnVudGltZS5zcGFuT2aP5+0SvkD7dFhnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL2dvZGVsdGFwcm9mL2ludGVybmFsL3Bwcm9mLigqcHJvZmlsZUJ1aWxkZXIpLnN0cmluZ0luZGV4kfFSfkqT/c0NcnVudGltZS5yZWFkeSA5y2577Vh6FXJ1bnRpbWUuKCpnY1dvcmspLnB1dJFjY7cnhgJpQGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9wcm90by4oKkJsb2NrKS5BcHBlbmTuIqTY5B+6gz1naXRodWIuY29tL0NsaWNrSG91c2UvY2gtZ28vcHJvdG8uKCpDb2xEYXRlVGltZSkuRGVjb2RlQ29sdW1u5P6FnGby6KE9Z2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvc2NyYXBlLm5ld1NjcmFwZVBvb2wuZnVuYzEuMia74WChzmAQEHJ1bnRpbWUuY2hhbnJlY3amxguJyez6RBFydW50aW1lLmdyb3dzbGljZRhEL494wpYRLG5ldC4oKlJlc29sdmVyKS5nb0xvb2t1cElQQ05BTUVPcmRlci5mdW5jMy4xGgloGYckEqpHZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby91cHN0cmVhbS9yZW1vdGUuKCpSZW1vdGUpLnVwbG9hZFByb2ZpbGXmWy3acgipcFFnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLmJhdGNoU2FtcGxlc0FuZFRpbWVTZXJpZXNl8WFGer0aLBxydW50aW1lLm1lbWNsck5vSGVhcFBvaW50ZXJzqxm23pcDK90RcnVudGltZS5ydW5HQ1Byb2dVt8SFEJ/dQ1Fnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9wcm9jZXNzb3IvcHJvY2Vzc29yaGVscGVyLk5ld01ldHJpY3NQcm9jZXNzb3IuZnVuYzFj6Ba9dsbVaTdnaXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9mbGF0ZS4oKmNvbXByZXNzb3IpLmNsb3Nl0nbloM8jK5IbcnVudGltZS5tYXBhc3NpZ25fZmFzdDY0cHRyTTaYGWYzxNsUcnVudGltZS5sb2NrV2l0aFJhbms27TmsGdFln0pnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci5uZXdRdWV1ZVNlbmRlci5mdW5jMdUdZfSJPqEUF25ldC9odHRwLigqQ2xpZW50KS5zZW5kKiYD0avBXyMVbmV0L2h0dHAuKCpDbGllbnQpLkRvXnhZ/+0+YxBQZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvcGRhdGEvcG1ldHJpYy5SZXNvdXJjZU1ldHJpY3NTbGljZS5Nb3ZlQW5kQXBwZW5kVG+00ZWJro51LiZydW50aW1lL2ludGVybmFsL2F0b21pYy4oKlVpbnQzMikuTG9hZCLDQ+OedctOW2dvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2ludGVybmFsL21lbW9yeWxpbWl0ZXIuKCpNZW1vcnlMaW1pdGVyKS5zdGFydE1vbml0b3JpbmcuZnVuYzGwZDyIuTF3OhhpbnRlcm5hbC9wb2xsLigqRkQpLkluaXTLV6mqEGla7RNydW50aW1lLmFkanVzdGZyYW1l80cdjE5muuoQcnVudGltZS5zZWxlY3Rnb3HrbMb7r59LMGdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28uKCpTZXNzaW9uKS5yZXNldBstzi0YQeFUC2Flc2hhc2hib2R5t5CK91yEWDpIZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvcmVjZWl2ZXIvcmVjZWl2ZXJoZWxwZXIuKCpPYnNSZXBvcnQpLmVuZE9wJqIagOdV/H0QcnVudGltZS5jaGFuc2VuZOdhlfbYvkfkGnJ1bnRpbWUuUmVhZE1lbVN0YXRzLmZ1bmMxsztcCXJDfJkUcnVudGltZS5lbnRlcnN5c2NhbGw2tVEUA9HMSDVnaXRodWIuY29tL0NsaWNrSG91c2UvY2gtZ28vcHJvdG8uQ29sU3RyLkVuY29kZUNvbHVtbm41kBZcETesQmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpTdHJpbmcpLkVuY29kZcMD/mcNHsdfGHJ1bnRpbWUuKCpibWFwKS5vdmVyZmxvd0JhXO0lYaPtHHJ1bnRpbWUuc3RvcFRoZVdvcmxkV2l0aFNlbWFVueOTabPz9BhydW50aW1lLmV2YWN1YXRlX2Zhc3RzdHKMoFlkje2cCBFydW50aW1lLnN0ZWFsV29yaz7FihbCxxnQFnJ1bnRpbWUuZnVuY0luZm8uZW50cnnoRCkkx6KpdBlydW50aW1lLm1hcGFzc2lnbl9mYXN0c3RyeEQPvddLw1g6Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpiYXRjaCkuY2xvc2VRdWVyeVrypnOGyBzxeGdpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9yZWNlaXZlci9wcm9tZXRoZXVzcmVjZWl2ZXIvaW50ZXJuYWwuKCptZXRyaWNHcm91cCkuc29ydFBvaW50c5p6Lp9WMIYTCnNvcnQuU2xpY2XcmK5ZXGed119naXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvcmVjZWl2ZXIvcHlyb3Njb3BlcmVjZWl2ZXIuKCpweXJvc2NvcGVSZWNlaXZlcikub3Blbk11bHRpcGFydBJGN95nK2BiIGludGVybmFsL2J5dGVhbGcuSW5kZXhCeXRlU3RyaW5nIqWIrazZeXIZY29udGV4dC5XaXRoRGVhZGxpbmVDYXVzZbjK/kB+2befDnN5c2NhbGwuc29ja2V0a0TqeVTumAAicnVudGltZS9pbnRlcm5hbC9zeXNjYWxsLkVwb2xsV2FpdOjP4eecILfxD3J1bnRpbWUuZ29yZWFkee7HASIibT0qIXJ1bnRpbWUvaW50ZXJuYWwvc3lzY2FsbC5FcG9sbEN0bD9H6u5DnUcLM2dvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3BkYXRhL3Bjb21tb24uTWFwLkdldFZfiVE5t4IHFWJ5dGVzLigqQnVmZmVyKS5Xcml0ZSWNw7QPJRzdEnJ1bnRpbWUuYXJlbmFJbmRleEREGx7ntrLyDnJ1bnRpbWUudGdraWxsjRw60lMiSdAOcnVudGltZS5wYXJrX23X5YeGExoFxhRydW50aW1lLmZpbmRSdW5uYWJsZdAxQmMhXlHaRGdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vdXBzdHJlYW0vcmVtb3RlLigqUmVtb3RlKS5zYWZlVXBsb2FkbPgvkiw14M9AZ2l0aHViLmNvbS9rbGF1c3Bvc3QvY29tcHJlc3MvZmxhdGUuKCpodWZmbWFuQml0V3JpdGVyKS5nZW5lcmF0ZXLF75xlFpmsLWdpdGh1Yi5jb20va2xhdXNwb3N0L2NvbXByZXNzL2ZsYXRlLk5ld1dyaXRlctQ4biXbXBBZPGdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS4oKnNjcmFwZUxvb3ApLmFwcGVuZKtszRUpwr84UmdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9yZWNlaXZlci9weXJvc2NvcGVyZWNlaXZlci5wb3N0UHJvY2Vzc1Byb2YuZnVuYzLVRVydtSUkgh5uZXQvaHR0cC5pc0tub3duSW5NZW1vcnlSZWFkZXKTp35NiG7pKUNnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9zY3JhcGUuKCpUYXJnZXQpLkxhYmVsc1JhbmdlLmZ1bmMxjxk8xF6hp9UQbmV0LigqY29ubikuUmVhZBD8kMwOtjQ7EnJ1bnRpbWUuZ3JleW9iamVjdAHrf6wTytYXdWdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9jbGlja2hvdXNlcHJvZmlsZWV4cG9ydGVyL2NoLigqY2xpY2tob3VzZUFjY2Vzc05hdGl2ZUNvbHVtbmFyKS5JbnNlcnRCYXRjaJxi084F3TO8EnNvcnQuaW5zZXJ0aW9uU29ydDJfqdHAKUdtEXJ1bnRpbWUucHJvY3lpZWxkcnj7Q0HFbboTcnVudGltZS5oZWFwU2V0VHlwZc9TQmJoYcNwe2dpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9yZWNlaXZlci9wcm9tZXRoZXVzcmVjZWl2ZXIvaW50ZXJuYWwuKCptZXRyaWNGYW1pbHkpLmFwcGVuZE1ldHJpY35gBQDXxaXSY2dpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIuKCptZXRyaWNzRXhwb3J0ZXIpLmV4cG9ydEhpc3RvZ3JhbURhdGFQb2ludMVOi6ZAhQeySGdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2NvbnN1bWVyLkNvbnN1bWVNZXRyaWNzRnVuYy5Db25zdW1lTWV0cmljc+Aoxjhu5LK4GG5ldC4oKlJlc29sdmVyKS5leGNoYW5nZY3Yx8DIjQwiYWdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIuKCptZXRyaWNzRXhwb3J0ZXIpLmV4cG9ydE51bWJlckRhdGFQb2ludHNagOKU+fSWqyNyZWdleHAuKCpSZWdleHApLkZpbmRBbGxTdHJpbmdJbmRleD5zwuycVct2Fm5ldC9odHRwLnBhcnNlUG9zdEZvcm0HRAFnPrK9niZuZXQvaHR0cC4oKlJlcXVlc3QpLlBhcnNlTXVsdGlwYXJ0Rm9ybaXte8Z1guYAFG5ldC4oKm5ldEZEKS5jb25uZWN07cPND2dAeCWGAXNsaWNlcy5Tb3J0RnVuY1tnby5zaGFwZS5bXWVuY29kaW5nL2pzb24ucmVmbGVjdFdpdGhTdHJpbmcsZ28uc2hhcGUuc3RydWN0IHsgZW5jb2RpbmcvanNvbi52IHJlZmxlY3QuVmFsdWU7IGVuY29kaW5nL2pzb24ua3Mgc3RyaW5nIH1doQ7y57WDN24dcnVudGltZS5nY0RyYWluTWFya1dvcmtlcklkbGVyUExUskaRPxJuZXQuaW50ZXJuZXRTb2NrZXQUymKjbGZpbhJydW50aW1lLnNjYW5vYmplY3R1VHScIpym/zVnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9wZGF0YS9wY29tbW9uLk1hcC5SYW5nZfr7XAezXLUQUmdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vZ29kZWx0YXByb2YvaW50ZXJuYWwvcHByb2YuKCpwcm9maWxlQnVpbGRlcikuQnVpbGQBkwJNtlVxnkFnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmNsaWNraG91c2UpLlByZXBhcmVCYXRjaLKfI1ADjjp5RGdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vdXBzdHJlYW0vcmVtb3RlLigqUmVtb3RlKS5oYW5kbGVKb2Jze9ZW61CpL244Z2l0aHViLmNvbS9wcm9tZXRoZXVzL2NvbW1vbi9tb2RlbC5sYWJlbFNldFRvRmluZ2VycHJpbnRwC8TK+F/Z0A5ydW50aW1lLnN0YXJ0bfmqKJXaA3cYDW5ldC9odHRwLnNlbmSrd7DdFGvomxplbmNvZGluZy9iaW5hcnkuUmVhZFZhcmludKx/yn3cmVuZT2dvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2V4cG9ydGVyL2V4cG9ydGVyaGVscGVyLigqT2JzUmVwb3J0KS5FbmRNZXRyaWNzT3Ba6sJ3kzMCPEFnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqQXJyYXkpLmFwcGVuZMiMVIvPNXcSDXJ1bnRpbWUubG9jazIMIJIkJ+C6MA5pby5SZWFkQXRMZWFzdKFZgoVxW3jKD3J1bnRpbWUucGN2YWx1Zbywal2vzkeKGGlvLigqTGltaXRlZFJlYWRlcikuUmVhZNoMFcEX239ZEXNvcnQucGRxc29ydF9mdW5jY6ah6SjAVDobbmV0Ligqc3lzRGlhbGVyKS5kaWFsU2VyaWFs43DMjxLDab9hc2xpY2VzLnBkcXNvcnRDbXBGdW5jW2dvLnNoYXBlLnN0cnVjdCB7IGVuY29kaW5nL2pzb24udiByZWZsZWN0LlZhbHVlOyBlbmNvZGluZy9qc29uLmtzIHN0cmluZyB9XVfbWLBj/r1qEXJ1bnRpbWUubWFrZXNsaWNlbMbwg+GZ/WpFZ2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvc2NyYXBlLigqc2NyYXBlTG9vcCkuc2NyYXBlQW5kUmVwb3J0jwWHYhXJkbk4Z2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvbW9kZWwvbGFiZWxzLk5ld0J1aWxkZXJ1Z5i4VKo2oxZydW50aW1lLigqbGZzdGFjaykucG9wykbbjAfwBmMacnVudGltZS5zZW5kLmdvcmVhZHkuZnVuYzECbbOVbwmfTxdyZWZsZWN0LigqTWFwSXRlcikuTmV4dNKz1zttyvR8EXN0cmluZ3MuSW5kZXhCeXRl5Z3qGcQ1UrxEZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL2NvbHVtbi4oKkRhdGVUaW1lKS5EZWNvZGX317MU8G4OJhlydW50aW1lLigqZ2NXb3JrKS5iYWxhbmNlYH1HQVFuergYcmVnZXhwLigqYml0U3RhdGUpLnJlc2V0E4miI5YNKpFmZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL2NsaWNraG91c2Vwcm9maWxlZXhwb3J0ZXIuKCpjbGlja2hvdXNlUHJvZmlsZUV4cG9ydGVyKS5zZW5kjE0xodQNHo8ScnVudGltZS5wcm9jcmVzaXplHQ8qYC7zV6BFZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci5idWlsZExhYmVsU2V0GM3/cHirFUlfZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyL2NvbXByZXNzLigqRGVjb21wcmVzc29yKS5yZWFkQnl0ZXOdqPrBpq+OzRFydW50aW1lLm1lbWhhc2g2NFaoK13d5M2kG3J1bnRpbWUubm90ZXRzbGVlcF9pbnRlcm5hbGFAN6KZAneWF3NvcnQuaW5zZXJ0aW9uU29ydF9mdW5jKG8VM69UXBEYcnVudGltZS5ncm93V29ya19mYXN0c3RyyXq6VWYqYEMQcnVudGltZS5uZXdzdGFja3iANjdmEa8SEW5ldC4oKm5ldEZEKS5kaWFspVaUZXD8Ws4QcnVudGltZS5ydW5xZ3JhYoybRzEJuDnVDHJ1bnRpbWUuYWRkYoG//wcmlW+VOWdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS4oKnNjcmFwZUxvb3ApLnJ1bntFGUOW1AqiRGdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vZ29kZWx0YXByb2YvaW50ZXJuYWwvcHByb2YuYWxsRnJhbWVzA+E2AtW5hX46Z2l0aHViLmNvbS9rbGF1c3Bvc3QvY29tcHJlc3MvZmxhdGUuKCpkZWNvbXByZXNzb3IpLmRvU3RlcIWSzw9CZV6IE3J1bnRpbWUucGFnZUluZGV4T2ZNiuFOrvCgsRZydW50aW1lLnJlYWRtZW1zdGF0c19ttmOZvFfQUXcNaW8uY29weUJ1ZmZlcvHJ7wB3Sa9QRmdpdGh1Yi5jb20va2xhdXNwb3N0L2NvbXByZXNzL2ZsYXRlLigqZGVjb21wcmVzc29yKS5odWZmbWFuQnVmaW9SZWFkZXK2lxdms+v10RdydW50aW1lLmZsdXNoYWxsbWNhY2hlcyHsDLRqK45nFnJ1bnRpbWUucmVlbnRlcnN5c2NhbGxKNFeV64vQUxxlbmNvZGluZy9iaW5hcnkuQXBwZW5kVmFyaW50ilyPvunMdHoZcnVudGltZS50eXBlUG9pbnRlcnMubmV4dKZed7sCe/ClKmdpdGh1Yi5jb20vcHJvbWV0aGV1cy9jb21tb24vbW9kZWwuaGFzaEFkZHi9Z2L8BnXMEnJ1bnRpbWUubWFwYWNjZXNzMSet0EnOSi3PUGdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3JlY2VpdmVyL3JlY2VpdmVyaGVscGVyLigqT2JzUmVwb3J0KS5yZWNvcmRNZXRyaWNzkPt1VpaW11cTcnVudGltZS5yZWFkVWludHB0cqBnooEl0l6/D3J1bnRpbWUuaGFuZG9mZv+mS68VFCQYR2dpdGh1Yi5jb20va2xhdXNwb3N0L2NvbXByZXNzL2ZsYXRlLigqZGVjb21wcmVzc29yKS5odWZmbWFuQmxvY2tEZWNvZGVyZNVqWLIeIDFcZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvZXhwb3J0ZXIvZXhwb3J0ZXJoZWxwZXIuKCptZXRyaWNzU2VuZGVyV2l0aE9ic2VydmFiaWxpdHkpLnNlbmRdpUBGSIj93illbmNvZGluZy9qc29uLigqZW5jb2RlU3RhdGUpLnJlZmxlY3RWYWx1ZSACf0RcWXGqEXJ1bnRpbWUubWFwYXNzaWdueXDSATRAEhVPZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvcmVjZWl2ZXIvcmVjZWl2ZXJoZWxwZXIuKCpPYnNSZXBvcnQpLkVuZE1ldHJpY3NPcPlEKmefj+MiFm5ldC9odHRwLigqY29ubikuc2VydmWlacs9LptuDTVnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLigqUHJvZmlsZSkuQ2hlY2tWYWxpZE2rVs36YhM3FnJlZmxlY3QuKCpNYXBJdGVyKS5LZXlM/u9O9sACUh1ydW50aW1lLnN0YXJ0VGhlV29ybGRXaXRoU2VtYeQFfHBegZOLGnJ1bnRpbWUuZGVkdWN0QXNzaXN0Q3JlZGl0S1cekAHePHoOc3lzY2FsbC5Tb2NrZXRqIJ43AdmxyyhydW50aW1lLigqbXNwYW4pLnR5cGVQb2ludGVyc09mVW5jaGVja2Vk0EIa/refsg8KaW8uUmVhZEFsbEcS5eClewlyHGNvbXByZXNzL2d6aXAuKCpSZWFkZXIpLlJlYWTYS1XuP9msnhJzeXNjYWxsLlJhd1N5c2NhbGzWMBrRB2IFQmdzbGljZXMuaW5zZXJ0aW9uU29ydENtcEZ1bmNbZ28uc2hhcGUuc3RydWN0IHsgZW5jb2RpbmcvanNvbi52IHJlZmxlY3QuVmFsdWU7IGVuY29kaW5nL2pzb24ua3Mgc3RyaW5nIH1dHnazW7PsDQNPZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvZXhwb3J0ZXIvZXhwb3J0ZXJoZWxwZXIuKCpiYXNlUmVxdWVzdFNlbmRlcikuc2VuZK8kCSF10Sb1H25ldC9odHRwLigqVHJhbnNwb3J0KS5yb3VuZFRyaXAOXWN+AA9KcTlnby5vcGVudGVsZW1ldHJ5LmlvL2NvbnRyaWIvenBhZ2VzLigqU3BhblByb2Nlc3NvcikuT25FbmQegwRk2MiQWRtyZWdleHAuKCpSZWdleHApLmFsbE1hdGNoZXO2IoDxNjMcFhpydW50aW1lLigqbWhlYXApLmFsbG9jU3BhbsBIvrVtMpCKLWdpdGh1Yi5jb20vZ29vZ2xlL3Bwcm9mL3Byb2ZpbGUuZGVjb2RlTWVzc2FnZaui8EyD3+S2KXJ1bnRpbWUuKCpnY0NvbnRyb2xsZXJTdGF0ZSkuZW5saXN0V29ya2Vyfluu2FB9MM0McnVudGltZS5tcHV0vXdCB2Fyj2kjcnVudGltZS4oKnVud2luZGVyKS5yZXNvbHZlSW50ZXJuYWwJylTUF5mfwiNjb21wcmVzcy9mbGF0ZS4oKmRlY29tcHJlc3NvcikuUmVhZGymHc/Ad5l5FnJ1bnRpbWUuKCpnY0JpdHMpLmJpdHCQMVduJtsLY0VnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqU3RyaW5nKS5BcHBlbmRSb3fhxfW0jPt/mQ1ydW50aW1lLnN0b3Btlp4KlPgboIYYcnVudGltZS4oKnVud2luZGVyKS5pbml0lM8DDVzAGh1AZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL3Byb3RvLigqQmxvY2spLkRlY29kZcIiqcYvRxaMPWdpdGh1Yi5jb20va2xhdXNwb3N0L2NvbXByZXNzL2ZsYXRlLigqZGVjb21wcmVzc29yKS5uZXh0QmxvY2v4hyrG2t9TYwxzb3J0LnBkcXNvcnRxG1NqYFrw8hNyZWZsZWN0Lm1hcGl0ZXJuZXh0lKxkAiT6KVRMZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvcGRhdGEvcG1ldHJpYy5OdW1iZXJEYXRhUG9pbnRTbGljZS5BcHBlbmRFbXB0eUnfDj6hKUGYE3J1bnRpbWUuZ29zY2hlZEltcGzeeaUJ6I+ADUBnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9wZGF0YS9wbWV0cmljLk1ldHJpYy5TZXRFbXB0eUdhdWdlTe76i9L+SCtFZ2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvbW9kZWwvdGV4dHBhcnNlLigqUHJvbVBhcnNlcikuTWV0cmlj0Fs3gn1wpktBZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL2NvbHVtbi4oKkFycmF5KS5BcHBlbmSSkZjMM1e5E11naXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvcmVjZWl2ZXIvcHlyb3Njb3BlcmVjZWl2ZXIvcHByb2ZwYXJzZXIuKCpwUHJvZlBhcnNlcikuUGFyc2XhStFebo1tbw9ydW50aW1lLnNpZ25hbE3luQTS3KLf2hBydW50aW1lLnB1dGVtcHR5uw20YjmvCZI0Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpiYXRjaCkuU2VuZOKaiUS2U93wS2dvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2V4cG9ydGVyL2V4cG9ydGVyaGVscGVyLigqdGltZW91dFNlbmRlcikuc2VuZNnu4qi07LZBC2lvLlJlYWRGdWxsF3qlBrgCg34bZW5jb2RpbmcvanNvbi5zdHJpbmdFbmNvZGVyJFWdXKyFNSVgZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyL2NvbXByZXNzLigqRGVjb21wcmVzc29yKS5EZWNvbXByZXNzn0cPG3qBY2peZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby9nb2RlbHRhcHJvZi9pbnRlcm5hbC9wcHJvZi4oKkRlbHRhSGVhcFByb2ZpbGVyKS5Xcml0ZUhlYXBQcm90b1zZ14+h/xuPPGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY29ubmVjdCkuZmlyc3RCbG9ja8hEQut8+1ZbEXJ1bnRpbWUuY29weXN0YWNrtzR8hUOxOkFfZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvcGRhdGEvaW50ZXJuYWwvZGF0YS9wcm90b2dlbi9jb21tb24vdjEuKCpBbnlWYWx1ZSkuR2V0U3RyaW5nVmFsdWWm6tgdTYsP1ltnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci4oKmxvZ3NFeHBvcnRlcldpdGhPYnNlcnZhYmlsaXR5KS5zZW5kcf7Fu/fQRVFXZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL2NsaWNraG91c2Vwcm9maWxlZXhwb3J0ZXIvY2gucmVhZFRyZWVGcm9tTWFwpBEJjMmnUVxAZ2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvc2NyYXBlLm11dGF0ZVJlcG9ydFNhbXBsZUxhYmVscz2cWNhqEPJTP2dpdGh1Yi5jb20va2xhdXNwb3N0L2NvbXByZXNzL2ZsYXRlLigqaHVmZm1hbkVuY29kZXIpLmJpdENvdW50c44mpXyGCYhiGm5ldC4oKlJlc29sdmVyKS50cnlPbmVOYW1lw81YPvZGX+0PcnVudGltZS5nY0RyYWluuZEeoKOMMzoScnVudGltZS50eXBlQXNzZXJ0f1499lZIPiMrZ2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS5pbml0LmZ1bmMzNzfbAGvT61NQGnJ1bnRpbWUuKCpfZnVuYykuaXNJbmxpbmVkz7tq3jZIVoA9Z28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvZXhwb3J0ZXIvZXhwb3J0ZXJoZWxwZXIuZW5kU3BhbgoVOlOd6TwIFnJ1bnRpbWUuZ2NCZ01hcmtXb3JrZXLpsCOxr9VmyRRydW50aW1lLnN0b3BUaGVXb3JsZDCskYca6cckF3J1bnRpbWUuKCpzcGFuU2V0KS5wdXNoG3akRvjvQv5CZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL2NvbHVtbi4oKkVudW04KS5TY2FuUm93IzEir84N79kfbmV0L2h0dHAuKCpUcmFuc3BvcnQpLlJvdW5kVHJpcLGYFjEZdNRwTmdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2V4cG9ydGVyL2V4cG9ydGVyaGVscGVyLigqbWV0cmljc1JlcXVlc3QpLkV4cG9ydBlezRHBe9WjPGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqYmF0Y2gpLkFwcGVuZFN0cnVjdDKvgezXfoSWFXJ1bnRpbWUuY29uY2F0c3RyaW5nc6ksmAXwqTabIXJ1bnRpbWUvaW50ZXJuYWwvc3lzY2FsbC5TeXNjYWxsNvkfAsiVQ7scEnJ1bnRpbWUucmVhZHZhcmludDgSHaZ7pavMIm5ldC9odHRwLigqcmVzcG9uc2UpLmZpbmlzaFJlcXVlc3SNPs/A0OxGUwxzeXNjYWxsLnJlYWQdF+dCM5L6Mw9zeXNjYWxsLlN5c2NhbGwznk7U9cAkyTlnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmNvbm5lY3QpLnByb2Nlc3NsimGjOL2eIDFnaXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9nemlwLigqUmVhZGVyKS5SZWFka5gDYPkVCG+bAWdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2V4cG9ydGVyL2ludGVybmFsL3F1ZXVlLigqYm91bmRlZE1lbW9yeVF1ZXVlW2dvLnNoYXBlLmludGVyZmFjZSB7IEV4cG9ydChjb250ZXh0LkNvbnRleHQpIGVycm9yOyBJdGVtc0NvdW50KCkgaW50IH1dKS5Db25zdW1lzTduQC7IvGsRcnVudGltZS5GdW5jRm9yUEO4zles2qhBsw9ydW50aW1lLm5ldHBvbGy2xqYIqOnAXhhyZWZsZWN0LigqTWFwSXRlcikuVmFsdWXYyas6zGuKChJydW50aW1lLmZ1dGV4c2xlZXA7Qg6tSD4gNxhydW50aW1lLigqdW53aW5kZXIpLm5leHS5xuaiwYlLwBpyZWdleHAuKCpSZWdleHApLmJhY2t0cmFja55UHHSxS7BvIW5ldC9odHRwLigqcGVyc2lzdENvbm4pLndyaXRlTG9vcGVjHTvvJLw/XmdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9yZWNlaXZlci9weXJvc2NvcGVyZWNlaXZlci4oKnB5cm9zY29wZVJlY2VpdmVyKS5yZWFkUHJvZmlsZXO6YrcGyA3h/A1ydW50aW1lLmZ1dGV4KG0JkeqaU0deZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyLigqcHlyb3Njb3BlUmVjZWl2ZXIpLmhhbmRsZS5mdW5jMd7fV1jzoLjOFG5ldC4oKlJlc29sdmVyKS5kaWFs9QGm631XjqwMcnVudGltZS5tZ2V0gRaReXlmJeERcnVudGltZS5yYXdzdHJpbmfolE9SCL2i3B5pbnRlcm5hbC9wb2xsLnJ1bnRpbWVfcG9sbE9wZW4jd7ETu2H1phdydW50aW1lLigqbGZzdGFjaykucHVzaFpSzKpKDPMvoQFnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcmVjZWl2ZXIvcHJvbWV0aGV1c3JlY2VpdmVyL2ludGVybmFsLigqbWV0cmljR3JvdXApLnRvRGlzdHJpYnV0aW9uUG9pbnQuKCptZXRyaWNHcm91cCkuc29ydFBvaW50cy5mdW5jMSpRe8A3X4PwGHJ1bnRpbWUuc3RhY2tjYWNoZV9jbGVhctlb9l0SSbepFnJ1bnRpbWUuKCptaGVhcCkuYWxsb2MwXtmq5s6NuiNlbmNvZGluZy9iaW5hcnkubGl0dGxlRW5kaWFuLlVpbnQ2NLWdYAhe/TbjRmdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS5tdXRhdGVSZXBvcnRTYW1wbGVMYWJlbHMuZnVuYzHZ8UHwc2oWLxJuZXQuKCpuZXRGRCkuV3JpdGUIouedHfF3iElnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci4oKnJldHJ5U2VuZGVyKS5zZW5kpq3W81AgmtkUYnl0ZXMuKCpCdWZmZXIpLlJlYWSutJwq7IXyuitnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLmluaXQuZnVuYzM2VqbWt4FhruU7Z2l0aHViLmNvbS9rbGF1c3Bvc3QvY29tcHJlc3MvZmxhdGUuKCpjb21wcmVzc29yKS5zdG9yZUZhc3QmMEU+cn+chVJnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL2dvZGVsdGFwcm9mL2ludGVybmFsL3Bwcm9mLigqcHJvZmlsZUJ1aWxkZXIpLmZsdXNoXHz0ySnHsrURbmV0LigqbmV0RkQpLlJlYWQSuVK4WYewjmRnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLigqbWV0cmljc0V4cG9ydGVyKS5leHBvcnRIaXN0b2dyYW1EYXRhUG9pbnRzlwxfZlJq98AVZW5jb2RpbmcvanNvbi5NYXJzaGFs8ZRk6cJ+c0pJZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL2NvbHVtbi4oKkFycmF5KS5hcHBlbmRSb3dQbGFpboHGHHDqL5PKJWdpdGh1Yi5jb20vZ28tZmFzdGVyL2NpdHkuaGFzaDEyOHRvNjQIwEsri+2Ih0NnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL2dvZGVsdGFwcm9mLigqSGVhcFByb2ZpbGVyKS5Qcm9maWxlQiQCXQ4hhvlFZ2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvc2NyYXBlLigqc2NyYXBlTG9vcCkuYWRkUmVwb3J0U2FtcGxlQpvOrnEC5VIPcmVmbGVjdC5jb3B5VmFsmSM2I+8EZcZZZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby9nb2RlbHRhcHJvZi9pbnRlcm5hbC9wcHJvZi4oKnByb2ZpbGVCdWlsZGVyKS5lbWl0TG9jYXRpb27kKCgBqHtIfCtjb21wcmVzcy9mbGF0ZS4oKmRlY29tcHJlc3NvcikuaHVmZm1hbkJsb2NryHH5IMz1bdUYYnl0ZXMuKCpCdWZmZXIpLlJlYWRGcm9t7En9KV6YTOYccnVudGltZS4oKnN3ZWVwTG9ja2VkKS5zd2VlcK5J2Y9lYMQXDXN5c2NhbGwud3JpdGWdT7Bfxnx6ZCJydW50aW1lLmdjRHJhaW5NYXJrV29ya2VyRGVkaWNhdGVkAEOjrwIwah8ZbmV0L2h0dHAuKCpSZXF1ZXN0KS53cml0ZSb3WRNj/VQOYGdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIuKCptZXRyaWNzRXhwb3J0ZXIpLmV4cG9ydE51bWJlckRhdGFQb2ludNkjCXuTHSUDFnJ1bnRpbWUuKCpGcmFtZXMpLk5leHRJoamZx8dbcRtydW50aW1lLnN0YXJ0VGhlV29ybGQuZnVuYzHlDd6WNcbNDQ9ydW50aW1lLmdvZXhpdDDseL9Vb6p3Jw9zdHJpbmdzLkNvbXBhcmUEuBUoG+a7+xdydW50aW1lLmdjRmx1c2hCZ0NyZWRpdIadauvDVYG2TGdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9yZWNlaXZlci9weXJvc2NvcGVyZWNlaXZlci5wb3N0UHJvY2Vzc1Byb2b60DLvY59YcxRidWZpby4oKlJlYWRlcikuUmVhZBIyaT2sTm02HXJ1bnRpbWUudHlwZVBvaW50ZXJzLm5leHRGYXN0OHpnJxRQo9UTcnVudGltZS5mdXRleHdha2V1cNgb7bsU17TYFHJ1bnRpbWUuc2VsZWN0bmJyZWN2HI96gr7orp8TcnVudGltZS5yb3VuZHVwc2l6ZXiWBt8Vuu2XGHJ1bnRpbWUubWFwYXNzaWduX2Zhc3Q2NN9HAeidKjSGFnJlZ2V4cC4oKlJlZ2V4cCkuU3BsaXTYer09rKL6gihjb21wcmVzcy9mbGF0ZS4oKmRlY29tcHJlc3NvcikubmV4dEJsb2NrbBnsSa6uT14acnVudGltZS5tYXJrQml0cy5zZXRNYXJrZWSUN1G5Bpdmzx1uZXQvaHR0cC4oKlJlcXVlc3QpLlBhcnNlRm9ybaSWh0tCJk4pH3J1bnRpbWUuKCptY2VudHJhbCkudW5jYWNoZVNwYW6F/axaFAZ6WxRydW50aW1lLm5leHRGcmVlRmFzdMUegS5iNcwmPmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY29ubmVjdCkucHJlcGFyZUJhdGNos1Dtsb87TWM8Z2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvbW9kZWwvbGFiZWxzLigqQnVpbGRlcikuRGVsmk4XSzYVVyMNcnVudGltZS5tUGFya8g6m4BlxqYBEW5ldC4oKmNvbm4pLldyaXRliYAoNgNtkdk3Z2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvbW9kZWwvdmFsdWUuSXNTdGFsZU5hTkXcO8gzTRfJeGdpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9yZWNlaXZlci9wcm9tZXRoZXVzcmVjZWl2ZXIvaW50ZXJuYWwuKCp0cmFuc2FjdGlvbikuZ2V0TWV0cmljc4IXBNH1lMtvKWdpdGh1Yi5jb20vZ29vZ2xlL3Bwcm9mL3Byb2ZpbGUudW5tYXJzaGFsPczaJBi8chVnZ2l0aHViLmNvbS9vcGVuLXRlbGVtZXRyeS9vcGVudGVsZW1ldHJ5LWNvbGxlY3Rvci1jb250cmliL3BrZy90cmFuc2xhdG9yL3Byb21ldGhldXMud29yZFRvVUNVTU9yRGVmYXVsdMKPWoQNhCwYRmdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL21vZGVsL2xhYmVscy4oKlNjcmF0Y2hCdWlsZGVyKS5MYWJlbHN8uSC3te6SNlxnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLigqbWV0cmljc0V4cG9ydGVyKS5jb2xsZWN0RnJvbU1ldHJpY0sKv/002FRnHXJ1bnRpbWUuKCptY2VudHJhbCkuY2FjaGVTcGFu65058wrY2+kPcnVudGltZS5iZ3N3ZWVw8xblPQF6X9gpZ2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS5QYXJzZURhdGFGPKTnSvZBJxpydW50aW1lLnN0b3BUaGVXb3JsZC5mdW5jMcR0RkzCbw+YG3J1bnRpbWUuZW50ZXJzeXNjYWxsX3N5c21vbvti8kBbKdiFCm5ldC5zb2NrZXRSAtDJWqJpyjdnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmJhdGNoKS5yZWxlYXNlGZEA7ML+AukjcnVudGltZS4oKm1zcGFuKS53cml0ZUhlYXBCaXRzU21hbGwYB0RLGoaC91dnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9pbnRlcm5hbC9mYW5vdXRjb25zdW1lci4oKm1ldHJpY3NDb25zdW1lcikuQ29uc3VtZU1ldHJpY3NRws6TIrggXDdnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKnN0cnVjdE1hcCkuTWFwLVC+2K79KVcYcnVudGltZS4oKm1jZW50cmFsKS5ncm93iTs1oz8F4vQVcnVudGltZS5zdGFydFRoZVdvcmxkqZwxP1Av1UsRcnVudGltZS5zZWx1bmxvY2v4Dclm+zUYczhnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvLigqU2Vzc2lvbikudGFrZVNuYXBzaG90c6n1a8KtZq/SEHJ1bnRpbWUubWFsbG9jZ2MZPIbLE+dNxERnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqQXJyYXkpLkFwcGVuZFJvdzu2SyNbL4c2NmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqYmF0Y2gpLkFwcGVuZMrtZYME9hTgFXJ1bnRpbWUubWFya3Jvb3RCbG9ja+amLDVsHwwbIG5ldC9odHRwLnBlcnNpc3RDb25uV3JpdGVyLldyaXRlhdX3VuV6AqQ6Z2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby4oKlNlc3Npb24pLmR1bXBIZWFwUHJvZmlsZfd+BDwJjVnSEXJ1bnRpbWUuZ29zY2hlZF9tUK5Jx0owvUoacnVudGltZS5tYXBhY2Nlc3MyX2Zhc3RzdHL0FXM049HETBVydW50aW1lLmNvbmNhdHN0cmluZzJdJGt6HfUCJh9lbmNvZGluZy9qc29uLm1hcEVuY29kZXIuZW5jb2RlWYSf9MuzSxk4Z2l0aHViLmNvbS9rbGF1c3Bvc3QvY29tcHJlc3MvZmxhdGUuKCpkZWNvbXByZXNzb3IpLlJlYWR0SzZAZBi3yj1naXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9zY3JhcGUubmV3U2NyYXBlUG9vbC5mdW5jMS4xTZR2Ltes0OAYcnVudGltZS4oKmdjV29yaykudHJ5R2V0o8C216hKIR0TbWltZS5QYXJzZU1lZGlhVHlwZVpQU4TQ5kGuHmludGVybmFsL3BvbGwuKCpwb2xsRGVzYykuaW5pdGtR4D8YRs0UE3J1bnRpbWUuc3lzdGVtc3RhY2ssTzgSyRh/uVpnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLigqbWV0cmljc0V4cG9ydGVyKS5wdXNoTWV0cmljc0RhdGFQvbnXthIpbl1naXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLigqbWV0cmljc0V4cG9ydGVyKS5jb2xsZWN0RnJvbU1ldHJpY3P3Sqs2BSzGjjdnby5vcGVudGVsZW1ldHJ5LmlvL290ZWwvc2RrL3RyYWNlLigqcmVjb3JkaW5nU3BhbikuRW5kFQvkHKWJbk46Z28ub3BlbnRlbGVtZXRyeS5pby9vdGVsL3Nkay9tZXRyaWMuKCppbnQ2NEluc3QpLmFnZ3JlZ2F0ZT2JCHg4rjujGGJ1ZmlvLigqUmVhZGVyKS5SZWFkQnl0ZQN8NNbAUF0+WWdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vZ29kZWx0YXByb2YvaW50ZXJuYWwvcHByb2YuKCpwcm9maWxlQnVpbGRlcikuTG9jc0ZvclN0YWNrTykauVEuTyoRcnVudGltZS5mdW5jbGluZTE3wHHL99E4NhVuZXQvaHR0cC4oKkNsaWVudCkuZG+f6hB1pviGgiRjb250ZXh0LigqY2FuY2VsQ3R4KS5wcm9wYWdhdGVDYW5jZWzmC0KrhlJMPyVnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLlBhcnNlZbW/ItM+13AScnVudGltZS5ub3RldHNsZWVwLO//re+hpH4PcnVudGltZS5nZXRpdGFicgmAN379PB4ZbmV0LigqRGlhbGVyKS5EaWFsQ29udGV4dCisGp7pgiw7EHJ1bnRpbWUuc3dlZXBvbmXLRfYfu5jNbglzb3J0LlNvcnR0NqBuHidlCCFydW50aW1lLigqc3dlZXBMb2NrZXIpLnRyeUFjcXVpcmX8bqvLPAErfCNuZXQvaHR0cC5jaGVja0Nvbm5FcnJvcldyaXRlci5Xcml0ZdF0DqAgh9XOEXJ1bnRpbWUucnVucXN0ZWFsFxW+8tl3mREkbmV0L2h0dHAuKCpUcmFuc3BvcnQpLnNldFJlcUNhbmNlbGVyGay1VpKJ+LY7Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjb25uZWN0KS5zZW5kUXVlcnnxDnHgdoaczUhnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9tb2RlbC90ZXh0cGFyc2UuKCpQcm9tUGFyc2VyKS5uZXh0VG9rZW5VXQQbde6iZz9naXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmNvbm5lY3QpLnByb2ZpbGVFdmVudHPnpYlAtOJZQBVydW50aW1lLigqbXNwYW4pLmJhc2UgjRBgIpsaAzZnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvLigqU2Vzc2lvbikuU3RhcnQuZnVuYzEeFibdAh1ugTpnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9tb2RlbC9sYWJlbHMuTGFiZWxzLlJhbmdlc9TFdLUrT2o4Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjb25uZWN0KS5oYW5kbGUJLJzor1HC/klnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9wcm9jZXNzb3IvYmF0Y2hwcm9jZXNzb3IuKCpzaGFyZCkuc3RhcnRMb29wBkeHDiOEL+cTc3lzY2FsbC5SYXdTeXNjYWxsNvh9uVZtcBB7DW5ldC5zeXNTb2NrZXTsn9FAvt6axUtnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9wcm9jZXNzb3IvYmF0Y2hwcm9jZXNzb3IuKCpzaGFyZCkucHJvY2Vzc0l0ZW2E8UYvXvNH5xRydW50aW1lLnBjZGF0YXZhbHVlMVyUQjcdXcp/FHJ1bnRpbWUuUmVhZE1lbVN0YXRz0Q3upRuCDnMYcnVudGltZS4oKm1jYWNoZSkucmVmaWxsesBnD1wxIqgQcnVudGltZS5zY2hlZHVsZX1JIRcgJHV4RGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpUdXBsZSkuQXBwZW5kUm93huEJxDTCfl8RcnVudGltZS5uZXdvYmplY3R7BmF7YzJrrhNydW50aW1lLmZsdXNobWNhY2hlVotoGob81nodbmV0L2h0dHAuKCpUcmFuc3BvcnQpLmdldENvbm6AsTb/INmQPpYBZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvZXhwb3J0ZXIvaW50ZXJuYWwvcXVldWUuKCpDb25zdW1lcnNbZ28uc2hhcGUuaW50ZXJmYWNlIHsgRXhwb3J0KGNvbnRleHQuQ29udGV4dCkgZXJyb3I7IEl0ZW1zQ291bnQoKSBpbnQgfV0pLlN0YXJ0LmZ1bmMxYHd1wACuCRAHY21wYm9keaC7+5EE0Rt+FHJlZmxlY3QudHlwZWRtZW1tb3ZlFRam6Bo/3PsUcnVudGltZS50eXBlZG1lbW1vdmVcZl6Ye5rtYiZjb21wcmVzcy9mbGF0ZS4oKmRlY29tcHJlc3NvcikuaHVmZlN5bckmZwlu+971E3J1bnRpbWUuZnVuY3NwZGVsdGFG3wEYthNxTTdnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmNvbm5lY3QpLmZsdXNoaj4O5cDsUyAMc3lzY2FsbC5SZWFkh1MLB/cnsD1SZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvaW50ZXJuYWwvbWVtb3J5bGltaXRlci4oKk1lbW9yeUxpbWl0ZXIpLnJlYWRNZW1TdGF0cwQNZU04QaxZPGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqYmF0Y2hDb2x1bW4pLkFwcGVuZL2c57X6J7pLQ2dvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3BkYXRhL3BtZXRyaWMuTWV0cmljU2xpY2UuQXBwZW5kRW1wdHnxt7K5GFSQKSRlbmNvZGluZy9qc29uLigqZW5jb2RlU3RhdGUpLm1hcnNoYWyMXcaD5qvkgg1zeXNjYWxsLldyaXRlB3YjQuZFJMY+Z2l0aHViLmNvbS9rbGF1c3Bvc3QvY29tcHJlc3MvZmxhdGUuKCpodWZmbWFuRW5jb2RlcikuZ2VuZXJhdGU8AJLYLLN/mxBydW50aW1lLm1hcmtyb290vroLKR+OzAYPcnVudGltZS5wdXRmdWxsbYz6VAS7xClLZ2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvc2NyYXBlLigqc2NyYXBlTG9vcCkuc2NyYXBlQW5kUmVwb3J0LmZ1bmMx/8+Qrm2l9xwRcnVudGltZS5zY2FuYmxvY2tMDfiNqq00vwtydW50aW1lLmFkZLxRhWFKG8DLHW5ldC4oKnN5c0RpYWxlcikuZGlhbFBhcmFsbGVs24Usp2X26F0zZ2l0aHViLmNvbS9rbGF1c3Bvc3QvY29tcHJlc3MvZmxhdGUuKCpXcml0ZXIpLkNsb3NlsScJFvdqzBYScnVudGltZS5ub3Rld2FrZXVwGv7dRogBa0MHaW8uQ29weS3jUMOau5IIF3J1bnRpbWUuc3Bhbk9mVW5jaGVja2VkLYm/+CpofN9LZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL2NvbHVtbi4oKkFycmF5KS5hcHBlbmRSb3dEZWZhdWx0w3v6nYmn07EyZ2l0aHViLmNvbS9wcm9tZXRoZXVzL2NvbW1vbi9tb2RlbC5MYWJlbE5hbWVzLkxlc3POpvz7GkSCizJnaXRodWIuY29tL0NsaWNrSG91c2UvY2gtZ28vcHJvdG8uKCpDb2xTdHIpLkFwcGVuZHa9OyQiw6yqOmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY29ubmVjdCkuc2VuZERhdGHWw8LQ7S5QKhJydW50aW1lLmZpbmRPYmplY3RzQ/iAz4K3jzxnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmNsaWNraG91c2UpLnJlbGVhc2XaVc/eEwOvuBBydW50aW1lLnByZWVtcHRNfSSWQQF57TgUY29udGV4dC5XaXRoRGVhZGxpbmWaF/a9mZZq6xpydW50aW1lLigqdW53aW5kZXIpLmluaXRBdIzjCXS65VefN2dpdGh1Yi5jb20vcHJvbWV0aGV1cy9jb21tb24vbW9kZWwuTGFiZWxTZXQuRmluZ2VycHJpbnSgbR/BHd3SAXRnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcmVjZWl2ZXIvcHJvbWV0aGV1c3JlY2VpdmVyL2ludGVybmFsLigqdHJhbnNhY3Rpb24pLkNvbW1pdExFNLHP+Of4GXJ1bnRpbWUubWFwYWNjZXNzMV9mYXN0NjQJOlC7aqL1rBVidWZpby4oKldyaXRlcikuRmx1c2jPkldGi9o7/AxydW50aW1lLnN0ZXCIL4qJQeItszpnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmNvbm5lY3QpLnJlYWREYXRhs1nrosMjvVAabmV0L2h0dHAubmV3VHJhbnNmZXJXcml0ZXLwsS0i19Vqa0ZnaXRodWIuY29tL3Byb21ldGhldXMvY29tbW9uL2NvbmZpZy4oKnVzZXJBZ2VudFJvdW5kVHJpcHBlcikuUm91bmRUcmlwBDO8shuC8a4ccnVudGltZS4oKm1oZWFwKS5hbGxvYy5mdW5jMbqRRtamXqcxGG5ldC4oKnN5c0RpYWxlcikuZGlhbFVEUK/s33W0xH5RVGdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2ludGVybmFsL21lbW9yeWxpbWl0ZXIuKCpNZW1vcnlMaW1pdGVyKS5DaGVja01lbUxpbWl0c70EAAAAAAAAAACeVBx0sUuwb0cvpyMsVu4AAAAAAAAAAACAw8kBAAAAAAAAAAAAAAAAGEQvj3jClhEGgrwfjqHvAAAAAAAAAAAAAC0xAQAAAAAAAAAAAAAAAICxNv8g2ZA+NToZfqc4+AAAAAAAAAAAAIA5ehIAAAAAAAAAAAAAAAAgjRBgIpsaA+jn8GTGa44AAAAAAAAAAAAAaIkJAAAAAAAAAAAAAAAAgb//ByaVb5XMa11kSQqSAAAAAAAAAAAAgCtTCwAAAAAAAAAAAAAAAAksnOivUcL+TuodZeig8gAAAAAAAAAAAICWmAAAAAAAAAAAAAAAAAAobQmR6ppTR27iu9hcprwAAAAAAAAAAAAADicHAAAAAAAAAAAAAAAAlLf3+v5y09GAA/yGjqfKAICWmAAAAAAAAGiJCQAAAAAAAAAAAAAAALKfI1ADjjp5yxu8F4RQ9wAAAAAAAAAAAICWmAAAAAAAAAAAAAAAAAAiw0PjnnXLTumje5XQSdoAAAAAAAAAAAAAh5MDAAAAAAAAAAAAAAAA+UQqZ5+P4yJd04dMoqbqAAAAAAAAAAAAgJaYAAAAAAAAAAAAAAAAAAoVOlOd6TwIXEXnPu9i8AAAAAAAAAAAAIC6lT4AAAAAAAAAAAAAAADrnTnzCtjb6WWMyG/RtaUAAAAAAAAAAAAAWmICAAAAAOjn8GTGa44A+A3JZvs1GHO30o44O6AZAQAAAAAAAAAAAGiJCQAAAADMa11kSQqSAGzG8IPhmf1qVPno2up7MwEAAAAAAAAAAIArUwsAAAAAZYzIb9G1pQAorBqe6YIsO1Lbd22GbEoBAAAAAAAAAAAAWmICAAAAAG7iu9hcprwAZWMdO+8kvD9G6ho9FgsfAQAAAAAAAAAAAA4nBwAAAACAA/yGjqfKAI0cOtJTIknQFxiNbq2SOAEAAAAAAAAAAIB3jgYAAAAAgAP8ho6nygD3fgQ8CY1Z0se5ANcm5kEBAAAAAAAAAACAw8kBAAAAAIAD/IaOp8oA5Q3eljXGzQ2dBvJkesRnAQAAAAAAAAAAgJaYAAAAAADpo3uV0EnaAK/s33W0xH5RxpTbjsSiOQEAAAAAAAAAAACHkwMAAAAAXdOHTKKm6gA4Eh2me6WrzKajxzSBRycBAAAAAAAAAACAlpgAAAAAAEcvpyMsVu4ACTpQu2qi9ayfa/87NMpdAQAAAAAAAAAAgJaYAAAAAABHL6cjLFbuAPNHHYxOZrrqQV/WHmEuOAEAAAAAAAAAAICWmAAAAAAARy+nIyxW7gAAQ6OvAjBqHzxrVbpYgHkBAAAAAAAAAACAlpgAAAAAAAaCvB+Ooe8AjialfIYJiGKdF9tKifYOAQAAAAAAAAAAAC0xAQAAAABcRec+72LwAGtR4D8YRs0UrYUtqZaFYwEAAAAAAAAAAIC6lT4AAAAATuodZeig8gDsn9FAvt6axUqP8llfRXUBAAAAAAAAAACAlpgAAAAAAMsbvBeEUPcA0DFCYyFeUdrcqdwAg+pNAQAAAAAAAAAAgJaYAAAAAAA1Ohl+pzj4AGuYA2D5FQhvMz7b6obJaAEAAAAAAAAAAIA5ehIAAAAAnRfbSon2DgHgKMY4buSyuOooOjCG7LABAAAAAAAAAAAALTEBAAAAALfSjjg7oBkBcetsxvuvn0tGZFNP0IvxAQAAAAAAAAAAAGiJCQAAAABG6ho9FgsfAYadauvDVYG2E9v/nAm06gEAAAAAAAAAAIDDyQEAAAAARuoaPRYLHwHcmK5ZXGed15X4XTLt65YBAAAAAAAAAACAlpgAAAAAAEbqGj0WCx8BSjRXleuL0FNdlwzDJpaPAYCWmAAAAAAAgJaYAAAAAABG6ho9FgsfASRVnVyshTUlnD/Fo/KCzwEAAAAAAAAAAIDDyQEAAAAARuoaPRYLHwGSkZjMM1e5E3stZSXvLb0BAAAAAAAAAAAAWmICAAAAAKajxzSBRycBCTpQu2qi9aw2kEu0FaDdAQAAAAAAAAAAgJaYAAAAAABU+eja6nszAdQ4biXbXBBZFUG8VMcn1AEAAAAAAAAAAABaYgIAAAAAVPno2up7MwF9JJZBAXntOJQrPgYKTKsBAAAAAAAAAACAlpgAAAAAAFT56NrqezMBbYz6VAS7xCmk2iZXvwqAAQAAAAAAAAAAALTEBAAAAABU+eja6nszARr+3UaIAWtDPjcNubfolwEAAAAAAAAAAICWmAAAAAAAVPno2up7MwHPVSBP+m3oYt0xXXrA/80BAAAAAAAAAAAALTEBAAAAAFT56NrqezMBKiYD0avBXyOAS0njYU++AQAAAAAAAAAAgMPJAQAAAABBX9YeYS44AamcMT9QL9VL2n0uMJaAwQGAlpgAAAAAAICWmAAAAAAAFxiNbq2SOAF6wGcPXDEiqM2zEBJFROYBAAAAAAAAAACAd44GAAAAAMaU247EojkBh1MLB/cnsD0RidO5VnS6AQAAAAAAAAAAAIeTAwAAAADHuQDXJuZBAUnfDj6hKUGYl7dNv11BpwEAAAAAAAAAAIDDyQEAAAAAUtt3bYZsSgF0NqBuHidlCEtjWqa/deYBAC0xAQAAAAAALTEBAAAAAFLbd22GbEoB7En9KV6YTOao8/net5GDAYCWmAAAAAAAAC0xAQAAAADcqdwAg+pNARoJaBmHJBKqXQvwWrEj9AEAAAAAAAAAAICWmAAAAAAAn2v/OzTKXQHmpiw1bB8MG4GOn0jS+9IBAAAAAAAAAACAlpgAAAAAAK2FLamWhWMBnU+wX8Z8emQuDFfFN8baAQAAAAAAAAAAAEbDIwAAAACthS2ploVjAaEO8ue1gzdu3gm2h9tk2wEAAAAAAAAAAIB00hoAAAAAnQbyZHrEZwF6wGcPXDEiqFe2YTt85vcBgJaYAAAAAACAlpgAAAAAADM+2+qGyWgBNu05rBnRZZ+C6DGyasH/AQAAAAAAAAAAgDl6EgAAAABKj/JZX0V1AV54Wf/tPmMQHZEHtYp3vgEAAAAAAAAAAICWmAAAAAAAPGtVuliAeQGzWeuiwyO9ULdxctKbBuYBAAAAAAAAAACAlpgAAAAAAKTaJle/CoABeXDSATRAEhXMQ8LoynFfAgAAAAAAAAAAgJaYAAAAAACk2iZXvwqAAaBtH8Ed3dIBPVyWihZUYwIAAAAAAAAAAIAdLAQAAAAAqPP53reRgwFNNpgZZjPE2/EOc2jUKDYCAAAAAAAAAACAlpgAAAAAAJX4XTLt65YBB0QBZz6yvZ7r0NQte2UPAgAAAAAAAAAAgJaYAAAAAAA+Nw25t+iXAbZjmbxX0FF3Nrpz3XC1FwIAAAAAAAAAAICWmAAAAAAAl7dNv11BpwGlYkFtlSd0BItswgxNVi8CAAAAAAAAAACAlpgAAAAAAJe3Tb9dQacBesBnD1wxIqiXDPGTH8xYAgAAAAAAAAAAAC0xAQAAAACUKz4GCkyrASKliK2s2Xly16vzoWB6ZwIAAAAAAAAAAICWmAAAAAAA6ig6MIbssAHe31dY86C4zmXf5fYLUCcCAAAAAAAAAAAALTEBAAAAABGJ07lWdLoBhuEJxDTCfl+m5eghzAcmAgAAAAAAAAAAgJaYAAAAAAARidO5VnS6AVyUQjcdXcp/rbymXdU9ZAIAAAAAAAAAAIDw+gIAAAAAey1lJe8tvQHmC0KrhlJMP+Hz61XAGC0CAAAAAAAAAAAAWmICAAAAAIBLSeNhT74BN8Bxy/fRODau/f3fDwh6AgAAAAAAAAAAgMPJAQAAAAAdkQe1ine+AabGC4nJ7PpEwY1zmabiYwIAAAAAAAAAAICWmAAAAAAA3TFdesD/zQH3ctP2qgjz6Ckbcs1NhGUCAAAAAAAAAAAALTEBAAAAAJw/xaPygs8BGM3/cHirFUn84iUMYhwGAgAAAAAAAAAAgMPJAQAAAACBjp9I0vvSAcg6m4BlxqYB7ISU4xJSMwIAAAAAAAAAAICWmAAAAAAAFUG8VMcn1AFN7vqL0v5IK7qrHe2kJ2gCAAAAAAAAAACAlpgAAAAAABVBvFTHJ9QBdEs2QGQYt8rD14TXcv1eAgAAAAAAAAAAgJaYAAAAAAAVQbxUxyfUAfEOceB2hpzNt/dD8/bWAwKAlpgAAAAAAICWmAAAAAAAFUG8VMcn1AHCj1qEDYQsGPTfrM7F3gACAAAAAAAAAACAlpgAAAAAAC4MV8U3xtoBw81YPvZGX+0NQvQ4Y3MbAoCWmAAAAAAAAEbDIwAAAADeCbaH22TbAcPNWD72Rl/tAmXaaV1xQgKA8PoCAAAAAIB00hoAAAAANpBLtBWg3QH8bqvLPAErfKh4MX+74FwCAAAAAAAAAACAlpgAAAAAALdxctKbBuYB1UVcnbUlJILHSfCPK3FRAgAAAAAAAAAAgJaYAAAAAADNsxASRUTmAdflh4YTGgXGdqtSBSljSwIAAAAAAAAAAIB3jgYAAAAAE9v/nAm06gF4lgbfFbrtl/5MZebPKXcCAAAAAAAAAACAlpgAAAAAABPb/5wJtOoBmnoun1YwhhNFUfJNmYt2AgAAAAAAAAAAgJaYAAAAAAAT2/+cCbTqAYHGHHDqL5PKEN/SbQLzVgKAlpgAAAAAAICWmAAAAAAARmRTT9CL8QEQR4RwgXBT0I6QZRe0uT8CAAAAAAAAAAAAaIkJAAAAAF0L8FqxI/QBKiYD0avBXyP696Gn8+AKAgAAAAAAAAAAgJaYAAAAAACC6DGyasH/AR52s1uz7A0DEYH0KA+OAQIAAAAAAAAAAIA5ehIAAAAA9N+szsXeAAKmxguJyez6RNEMO7jY2oYCAAAAAAAAAACAlpgAAAAAABGB9CgPjgECZNVqWLIeIDGQGR4x7VLUAgAAAAAAAAAAgN8XEAAAAAARgfQoD44BAqbq2B1Niw/W2GiHNl0U2gIAAAAAAAAAAABaYgIAAAAA/OIlDGIcBgLIcfkgzPVt1aU+ISBrobwCAAAAAAAAAACAw8kBAAAAAPr3oafz4AoCN8Bxy/fRODZsCOMiaWXyAgAAAAAAAAAAgJaYAAAAAADr0NQte2UPApQ3UbkGl2bP47sa2gKIygIAAAAAAAAAAICWmAAAAAAANrpz3XC1FwLIcfkgzPVt1Y5R+/OuoJ4CAAAAAAAAAACAlpgAAAAAAA1C9DhjcxsC99ezFPBuDibz0gwQg3aoAgAAAAAAAAAAAC0xAQAAAAANQvQ4Y3MbAjwAktgss3+bJkNCHMkssAIAAAAAAAAAAIDw+gIAAAAADUL0OGNzGwISMmk9rE5tNnR8Lx/OrdMCAC0xAQAAAAAALTEBAAAAAA1C9DhjcxsCFMpio2xmaW7d//Cpg9z2AgBoiQkAAAAAAGXNHQAAAACm5eghzAcmAqn1a8KtZq/S8LRjURPX0AIAAAAAAAAAAICWmAAAAAAAZd/l9gtQJwJyCYA3fv08HpnRgvpwX+ECAAAAAAAAAAAALTEBAAAAAOHz61XAGC0C8xblPQF6X9gLAZ4qtwGhAgAAAAAAAAAAgMPJAQAAAADh8+tVwBgtAtBCGv63n7IPDJA0c8Gu0gIAAAAAAAAAAICWmAAAAAAAi2zCDE1WLwJwC8TK+F/Z0EImT6MrirUCAAAAAAAAAACAlpgAAAAAAOyElOMSUjMC2fFB8HNqFi+eRTeysvDTAgAAAAAAAAAAgJaYAAAAAADxDnNo1Cg2AsiMVIvPNXcS54FXj9DrnAIAAAAAAAAAAICWmAAAAAAAjpBlF7S5PwKF1fdW5XoCpKIh/zOMuIUCAAAAAAAAAAAAaIkJAAAAAAJl2mldcUICTZR2Ltes0ODLG6aQ6j7AAgAAAAAAAAAAAC0xAQAAAAACZdppXXFCAvfXsxTwbg4mZDVYwIS5ugIAAAAAAAAAAABaYgIAAAAAAmXaaV1xQgIEuBUoG+a7+xN5J1IBi5cCgJaYAAAAAACAlpgAAAAAAAJl2mldcUICFMpio2xmaW4TgruzXjaiAgAOJwcAAAAAANASEwAAAAACZdppXXFCAhIyaT2sTm02gjvnw7U5yQKAlpgAAAAAAICWmAAAAAAAdqtSBSljSwLhxfW0jPt/mbjcX7L0cpgCAAAAAAAAAACAlpgAAAAAAHarUgUpY0sCmk4XSzYVVyNnsggZNLSZAgAAAAAAAAAAAC0xAQAAAAB2q1IFKWNLArjOV6zaqEGz7GYY5Zg56wIAAAAAAAAAAABaYgIAAAAAdqtSBSljSwKMoFlkje2cCAij1vXKdrUCgJaYAAAAAAAAWmICAAAAAMdJ8I8rcVECuZEeoKOMMzqiwQR0EsLiAgAAAAAAAAAAgJaYAAAAAACXDPGTH8xYAtflh4YTGgXGx8Ww3rl6/AIAAAAAAAAAAAAtMQEAAAAAqHgxf7vgXALIOpuAZcamAcMwvPI0udgCAAAAAAAAAACAlpgAAAAAAMPXhNdy/V4CjwWHYhXJkblfOphGmdC+AgAAAAAAAAAAgJaYAAAAAADMQ8LoynFfAreQivdchFg6uH4gfAB+jwIAAAAAAAAAAICWmAAAAAAAPVyWihZUYwJF3DvIM00XyQeQjTbadsQCAAAAAAAAAAAAh5MDAAAAAD1clooWVGMCGAdESxqGgvdsLvpkgDOwAgAAAAAAAAAAgJaYAAAAAADBjXOZpuJjAqn1a8KtZq/S+z7fFtxsqwKAlpgAAAAAAICWmAAAAAAArbymXdU9ZAJrUeA/GEbNFO6RoCcToM4CAAAAAAAAAACAw8kBAAAAAK28pl3VPWQCiTs1oz8F4vSU83MGGQDkAgAAAAAAAAAAgJaYAAAAAACtvKZd1T1kAumwI7Gv1WbJd6XxRgYWsgIAAAAAAAAAAICWmAAAAAAAKRtyzU2EZQJCJAJdDiGG+egtNNY32tUCAAAAAAAAAAAALTEBAAAAANer86FgemcCn+oQdab4hoIcKe9OqA6YAgAAAAAAAAAAgJaYAAAAAAC6qx3tpCdoAtKz1zttyvR8a3GwGPi11wIAAAAAAAAAAICWmAAAAAAARVHyTZmLdgLaDBXBF9t/WVuIpWIXELkCAAAAAAAAAACAlpgAAAAAAP5MZebPKXcCnaj6waavjs2K0ZkrVELFAoCWmAAAAAAAgJaYAAAAAACu/f3fDwh6AtUdZfSJPqEUzWAQcRoMtwIAAAAAAAAAAIDDyQEAAAAAoiH/M4y4hQLYenh/0ylv1UsCOQfogBcDAAAAAAAAAACAw8kBAAAAAKIh/zOMuIUCCMBLK4vtiIeVvbhnSNYYAwAAAAAAAAAAgKS/BwAAAADRDDu42NqGAqn1a8KtZq/S7gMBvJNXfAOAlpgAAAAAAICWmAAAAAAAuH4gfAB+jwInrdBJzkotz8Nk9pXa9TYDAAAAAAAAAACAlpgAAAAAABwp706oDpgCIAJ/RFxZcaptR2nvbnoVA4CWmAAAAAAAgJaYAAAAAAC43F+y9HKYAn5brthQfTDNSavYlNYOVgMAAAAAAAAAAICWmAAAAAAAZ7IIGTS0mQLQtZgxgpXYNNQxtcJBFy4DAAAAAAAAAAAALTEBAAAAAOeBV4/Q65wCMl+p0cApR21dKFzAEQcLA4CWmAAAAAAAgJaYAAAAAACOUfvzrqCeArywal2vzkeKVAbmPyPUEQMAAAAAAAAAAICWmAAAAAAACwGeKrcBoQKlacs9LptuDaV8JUDgbHQDAAAAAAAAAACAlpgAAAAAAAsBniq3AaECghcE0fWUy29WdpXEAtVPAwAAAAAAAAAAAC0xAQAAAAATgruzXjaiAvuNl6PuA6U51Ea1QbJHOAOAlpgAAAAAAICWmAAAAAAAE4K7s142ogIQ/JDMDrY0OzmgfxEVKDMDgJaYAAAAAACAlpgAAAAAABOCu7NeNqICbKYdz8B3mXn0cBtuRFRbA4DDyQEAAAAAgMPJAQAAAAATgruzXjaiAueliUC04llA5hArExhARwMAWmICAAAAAABaYgIAAAAAE4K7s142ogKKXI++6cx0eu7eurvqS0cDAFpiAgAAAAAAWmICAAAAABOCu7NeNqICJY3DtA8lHN3WGUEAhB9VA4CWmAAAAAAAgJaYAAAAAAATgruzXjaiAmognjcB2bHLt6U0cT8tLgMAAAAAAAAAAICWmAAAAAAAE4K7s142ogLWw8LQ7S5QKhIVIyqvYycDgPD6AgAAAACA8PoCAAAAAPPSDBCDdqgCoGeigSXSXr+b8Z0crjYkAwAAAAAAAAAAAC0xAQAAAAAmQ0IcySywAsrtZYME9hTgX51YQoNzHgMAAAAAAAAAAIDw+gIAAAAAbC76ZIAzsALFToumQIUHsh1tp3tJwBwDAAAAAAAAAACAlpgAAAAAAHel8UYGFrICa1HgPxhGzRRiBkPiWhAQAwAAAAAAAAAAgJaYAAAAAAAIo9b1yna1AtF0DqAgh9XOTFyzXQGrMgMAAAAAAAAAAIDDyQEAAAAAQiZPoyuKtQKxJwkW92rMFqqZD8fkIh0DAAAAAAAAAACAlpgAAAAAAM1gEHEaDLcC+aooldoDdxgQT3co7zEfAwAAAAAAAAAAgMPJAQAAAABbiKViFxC5AtoMFcEX239ZhAwQ2rLnEAMAAAAAAAAAAICWmAAAAAAAZDVYwIS5ugKrovBMg9/ktgcK9Z0TGiYDAAAAAAAAAAAALTEBAAAAAGQ1WMCEuboCoGeigSXSXr9v/zNBdIMFAwAAAAAAAAAAAC0xAQAAAAClPiEga6G8Arywal2vzkeKGnPI8JahcgMAAAAAAAAAAIDDyQEAAAAAXzqYRpnQvgJX21iwY/69aoff9xwtyTQDAAAAAAAAAACAlpgAAAAAAMsbppDqPsACdWeYuFSqNqNPdgu+NdIiA4CWmAAAAAAAgJaYAAAAAADLG6aQ6j7AAuW5BNLcot/aLQUl+9LDWQMAAAAAAAAAAICWmAAAAAAAB5CNNtp2xAK9nOe1+ie6S1vz3LZBYEkDAAAAAAAAAACAlpgAAAAAAAeQjTbadsQC3nmlCeiPgA0Vj6BGxtspAwAAAAAAAAAAgJaYAAAAAAAHkI022nbEAs9TQmJoYcNw3NOI6A+MNgMAAAAAAAAAAABaYgIAAAAA47sa2gKIygI+c8LsnFXLdi5NQqwHczMDAAAAAAAAAACAlpgAAAAAAO6RoCcToM4C52GV9ti+R+RWA5+drcseAwAAAAAAAAAAgMPJAQAAAADwtGNRE9fQAiHZ+HhuiwONnJVPdaGhLAMAAAAAAAAAAICWmAAAAAAADJA0c8Gu0gKmrdbzUCCa2arPhmZN0FADAAAAAAAAAACAlpgAAAAAAJ5FN7Ky8NMCjF3Gg+ar5IINFGDHcJFbAwAAAAAAAAAAgJaYAAAAAACQGR4x7VLUAqx/yn3cmVuZ2/MBK32ZOAMAAAAAAAAAAICWmAAAAAAAkBkeMe1S1AIIouedHfF3iDxGA4yCyBADAAAAAAAAAAAASX8PAAAAAOgtNNY32tUC5P6FnGby6KFI8SBk8itFAwAAAAAAAAAAAC0xAQAAAABrcbAY+LXXAhJGN95nK2BiOqmtWGCbMwOAlpgAAAAAAICWmAAAAAAAwzC88jS52ALZ8UHwc2oWL/oKn2sPeRADAAAAAAAAAACAlpgAAAAAANhohzZdFNoCCKLnnR3xd4hOn5fsaHVcAwAAAAAAAAAAAFpiAgAAAACZ0YL6cF/hArxRhWFKG8DLa3eD5RC3CwMAAAAAAAAAAAAtMQEAAAAAosEEdBLC4gIs7/+t76GkfusH1kpQ6yQDgJaYAAAAAACAlpgAAAAAAJTzcwYZAOQCa1HgPxhGzRS6SJaArPopAwAAAAAAAAAAgJaYAAAAAADsZhjlmDnrAmtE6nlU7pgABG4fwU3QawMAAAAAAAAAAABaYgIAAAAAbAjjImll8gLVHWX0iT6hFJTmtQPbCl8DAAAAAAAAAACAlpgAAAAAAN3/8KmD3PYCilyPvunMdHq1AGAiOn9zAwBoiQkAAAAAAGiJCQAAAADd//Cpg9z2AmognjcB2bHLwO5h3mquUQMALTEBAAAAAIDDyQEAAAAA3f/wqYPc9gKFks8PQmVeiKJcr+aR3UEDAC0xAQAAAAAALTEBAAAAAN3/8KmD3PYC1sPC0O0uUCrfrscXZN0rAwAtMQEAAAAAAC0xAQAAAADd//Cpg9z2AhIyaT2sTm026AeA6ErWSwOAlpgAAAAAAICWmAAAAAAA3f/wqYPc9gJsph3PwHeZea62z8d3VHADAFpiAgAAAAAAWmICAAAAAN3/8KmD3PYCkPt1VpaW11fLSnoJzzE8A4CWmAAAAAAAgJaYAAAAAADd//Cpg9z2AmwZ7Emurk9e8s9y6DbnFAOAlpgAAAAAAICWmAAAAAAA3f/wqYPc9gLnpYlAtOJZQMkmNaTzfUUDAFpiAgAAAAAAWmICAAAAAMfFsN65evwCTTaYGWYzxNsU2eC6tZomAwAAAAAAAAAAgJaYAAAAAADHxbDeuXr8AppOF0s2FVcjWBapwpIDJwMAAAAAAAAAAICWmAAAAAAAb/8zQXSDBQN1Z5i4VKo2o9nfSKVSYKADgJaYAAAAAACAlpgAAAAAAG//M0F0gwUDvroLKR+OzAZM7t5hoXbfAwAAAAAAAAAAgJaYAAAAAABrd4PlELcLA2OmoekowFQ6sB9kFl6nkQMAAAAAAAAAAAAtMQEAAAAAYgZD4loQEANGPKTnSvZBJ2UHBIxNxJEDAAAAAAAAAACAlpgAAAAAAPoKn2sPeRADjF3Gg+ar5IL79lWmV1bLAwAAAAAAAAAAgJaYAAAAAAA8RgOMgsgQA+KaiUS2U93wvKhqYQGC3wMAAAAAAAAAAABJfw8AAAAAhAwQ2rLnEAPaDBXBF9t/WdRQrwMo16IDAAAAAAAAAACAlpgAAAAAAFQG5j8j1BEDbIphozi9niADp9VzzLjbAwAAAAAAAAAAgJaYAAAAAABLAjkH6IAXAyisGp7pgiw7Jb5AT7SZwQOAlpgAAAAAAIDDyQEAAAAAlb24Z0jWGAOfRw8beoFjary/1zUXHZ4DAAAAAAAAAACApL8HAAAAAB1tp3tJwBwDxU6LpkCFB7LzNJP/OgqBAwAAAAAAAAAAgJaYAAAAAACqmQ/H5CIdAzh6ZycUUKPV3Gpvt8Hm+wMAAAAAAAAAAICWmAAAAAAAX51YQoNzHgP/z5CubaX3HCALGiCxUfEDAC0xAQAAAACA8PoCAAAAAFYDn52tyx4DTYrhTq7woLH3FD+nRu+EAwAAAAAAAAAAgMPJAQAAAAAQT3co7zEfA/CxLSLX1WprJ9VmZDlhswMAAAAAAAAAAIDDyQEAAAAAm/GdHK42JAO+ugspH47MBusXzfdYFucDAAAAAAAAAAAALTEBAAAAAAcK9Z0TGiYD2lXP3hMDr7iwBJt4brLNAwAAAAAAAAAAAC0xAQAAAAAU2eC6tZomA8iMVIvPNXcSxqIcoKBSkwMAAAAAAAAAAICWmAAAAAAAWBapwpIDJwPQtZgxgpXYNMqdNkhYF/QDgJaYAAAAAACAlpgAAAAAABWPoEbG2ykDhuEJxDTCfl/aH+bEgZKMAwAAAAAAAAAAgJaYAAAAAAC6SJaArPopA0mhqZnHx1txHtgOPFTe4QMAAAAAAAAAAICWmAAAAAAAnJVPdaGhLAPRDe6lG4IOc+2XJuvBdtgDAAAAAAAAAACAlpgAAAAAANQxtcJBFy4D2MmrOsxrigqU0yMftBDyAwAAAAAAAAAAAC0xAQAAAAC3pTRxPy0uA4ybRzEJuDnVH3hPac+HpgOAlpgAAAAAAICWmAAAAAAATFyzXQGrMgOlVpRlcPxazhInkZGb7PADgMPJAQAAAACAw8kBAAAAAC5NQqwHczMDo8C216hKIR0UapZIlCOiAwAAAAAAAAAAgJaYAAAAAACH3/ccLck0A6n1a8KtZq/SROxHiLfogwMAAAAAAAAAAICWmAAAAAAA3NOI6A+MNgOJgCg2A22R2TioaL7qf9oDgJaYAAAAAACAlpgAAAAAANzTiOgPjDYDlKxkAiT6KVRkiYIAFvjLAwAAAAAAAAAAgJaYAAAAAADc04joD4w2A1rypnOGyBzxNtBPjXnXoAMAAAAAAAAAAICWmAAAAAAA3NOI6A+MNgM9zNokGLxyFfK4WLpD28MDgJaYAAAAAACAlpgAAAAAAMNk9pXa9TYDFQvkHKWJbk7eWSD1+vvVA4CWmAAAAAAAgJaYAAAAAADb8wErfZk4A8+7at42SFaA8dEzsSp39gMAAAAAAAAAAICWmAAAAAAASPEgZPIrRQOkEQmMyadRXPG0FjQ8iZcDAAAAAAAAAAAALTEBAAAAAFvz3LZBYEkDpsYLicns+kT2E3iUjrPkAwAAAAAAAAAAgJaYAAAAAABWdpXEAtVPA8BIvrVtMpCKa4XZPtq7+wMAAAAAAAAAAAAtMQEAAAAAqs+GZk3QUAPCQG723G9R6i4jZ4S3Af4DgJaYAAAAAACAlpgAAAAAAMDuYd5qrlEDjJtHMQm4OdXeLVehNDH6A4CWmAAAAAAAgJaYAAAAAABJq9iU1g5WA7TRlYmujnUuEhLOgJVJ7AOAlpgAAAAAAICWmAAAAAAALQUl+9LDWQMjd7ETu2H1phlJe4X9x4kDgJaYAAAAAACAlpgAAAAAAA0UYMdwkVsDrknZj2VgxBeI969Rj+eRAwAAAAAAAAAAgJaYAAAAAABOn5fsaHVcA+KaiUS2U93wN6sQKJpr3QMAAAAAAAAAAABaYgIAAAAAlOa1A9sKXwP5qiiV2gN3GNH1iV2M8PwDAAAAAAAAAACAlpgAAAAAAARuH8FN0GsDqSyYBfCpNptx9yLEvl79AwBaYgIAAAAAAFpiAgAAAAAac8jwlqFyA0cS5eClewlyn0q2279f5AMAAAAAAAAAAIDDyQEAAAAApXwlQOBsdANMRTSxz/jn+MtMfZ3H+9wDAAAAAAAAAACAlpgAAAAAAPM0k/86CoEDVbfEhRCf3UPv5BZSpbRkBAAAAAAAAAAAgJaYAAAAAABE7EeIt+iDA+QFfHBegZOL2feoisIkFASAlpgAAAAAAICWmAAAAAAA9xQ/p0bvhAO2lxdms+v10SnsaypEzkkEAAAAAAAAAACAw8kBAAAAANof5sSBkowDqfVrwq1mr9LHQI5tkeZzBICWmAAAAAAAgJaYAAAAAACwH2QWXqeRA+7l3tsvRCodOVaHfNDiUgQAAAAAAAAAAAAtMQEAAAAAZQcEjE3EkQNCYVztJWGj7e0zhs4h2lYEAAAAAAAAAACAlpgAAAAAAIj3r1GP55EDHRfnQjOS+jOl7cN644Y/BAAAAAAAAAAAgJaYAAAAAADGohygoFKTAzJfqdHAKUdtBCaxJGzJIgSAlpgAAAAAAICWmAAAAAAA8bQWNDyJlwMeFibdAh1ugT92a6BzZi8EAAAAAAAAAAAALTEBAAAAALy/1zUXHZ4DA3w01sBQXT6bLd8BIHscBAAAAAAAAAAAgEpdBQAAAAC8v9c1Fx2eA803bkAuyLxr1x/cLyPSLgQAAAAAAAAAAICWmAAAAAAAvL/XNRcdngP6+1wHs1y1EPB7uWfj2mgEgJaYAAAAAACAw8kBAAAAADbQT41516ADmnoun1YwhhOPsXN3jQsXBAAAAAAAAAAAgJaYAAAAAAAUapZIlCOiAxeHlL3kfGJ37p2vt/ehXQQAAAAAAAAAAICWmAAAAAAA1FCvAyjXogPaDBXBF9t/WeTJZPperXYEAAAAAAAAAACAlpgAAAAAACfVZmQ5YbMDIzEir84N79nNFn3fF/QtBAAAAAAAAAAAgMPJAQAAAAAlvkBPtJnBA+xJ/SlemEzm/0KKBl1KZgSAlpgAAAAAAAAtMQEAAAAA+/ZVpldWywOuSdmPZWDEFy7VSwYeKhYEAAAAAAAAAACAlpgAAAAAAGSJggAW+MsDpsYLicns+kQTL6YfpOBXBAAAAAAAAAAAgJaYAAAAAACwBJt4brLNA+FK0V5ujW1vxm+cmqTBaQSAlpgAAAAAAAAtMQEAAAAA7Zcm68F22ANLCr/9NNhUZ6xhY5Q1QAMEAAAAAAAAAACAlpgAAAAAAAOn1XPMuNsDWYSf9MuzSxn+8STA99N5BAAAAAAAAAAAgJaYAAAAAADLTH2dx/vcA52o+sGmr47Nf62QvBZ7IwSAlpgAAAAAAICWmAAAAAAAN6sQKJpr3QNFYiBVOCYNuCFQxSYsnyAEAAAAAAAAAAAAWmICAAAAAEzu3mGhdt8DI3exE7th9aYub4GRPf4/BICWmAAAAAAAgJaYAAAAAAC8qGphAYLfA7GYFjEZdNRwo8WEhy3sYQQAAAAAAAAAAABJfw8AAAAAHtgOPFTe4QNM/u9O9sACUoG0FUnVSkEEAAAAAAAAAACAlpgAAAAAAJ9Kttu/X+QDCcpU1BeZn8KrrtBk4NolBAAAAAAAAAAAgMPJAQAAAAD2E3iUjrPkA6n1a8KtZq/SVj1m8JBMYAQAAAAAAAAAAICWmAAAAAAA6xfN91gW5wMjd7ETu2H1pjYcQPA6WSkEAC0xAQAAAAAALTEBAAAAACALGiCxUfED1sPC0O0uUCp0mUN+R9AxBICWmAAAAAAAgJaYAAAAAAAgCxogsVHxAxD8kMwOtjQ7GOUVy9ziRgQAAAAAAAAAAICWmAAAAAAAIAsaILFR8QOFks8PQmVeiLwBc88LOVIEgJaYAAAAAACAlpgAAAAAAJTTIx+0EPIDumK3BsgN4fwM/ppy7LsCBAAtMQEAAAAAAC0xAQAAAADx0TOxKnf2A/dKqzYFLMaOA/vSbxAOVAQAAAAAAAAAAICWmAAAAAAAa4XZPtq7+wOutJwq7IXyus1+nbUON3UEAAAAAAAAAACAlpgAAAAAAGuF2T7au/sDf1499lZIPiPv/PIxcMZWBAAAAAAAAAAAgJaYAAAAAADcam+3web7A7pitwbIDeH8nZHWeBB3VQSAlpgAAAAAAICWmAAAAAAA0fWJXYzw/AMjMSKvzg3v2dsU1vn8kxoEAAAAAAAAAACAlpgAAAAAAKxhY5Q1QAMELVC+2K79KVe+mMIAcSDwBAAAAAAAAAAAgJaYAAAAAAAu1UsGHioWBB0X50IzkvozI/vYarTMrgQAAAAAAAAAAICWmAAAAAAAj7Fzd40LFwTaDBXBF9t/WdgTx9AuHaYEAAAAAAAAAACAlpgAAAAAANsU1vn8kxoEryQJIXXRJvVr5YutXaCMBAAAAAAAAAAAgJaYAAAAAACbLd8BIHscBHtFGUOW1AqixPvkQHjJ9QQAAAAAAAAAAACHkwMAAAAAmy3fASB7HASP5+0SvkD7dDc5CgrbcvwEAAAAAAAAAACAlpgAAAAAAJst3wEgexwEmSM2I+8EZcZuIcEHYxjaBAAAAAAAAAAAAC0xAQAAAAAhUMUmLJ8gBBOJoiOWDSqRMHx7bJhI/AQAAAAAAAAAAABaYgIAAAAAq67QZODaJQTYer09rKL6gtpfKLLdR7YEAAAAAAAAAACAw8kBAAAAAM0Wfd8X9C0EryQJIXXRJvV9q2b9OdnaBAAAAAAAAAAAgMPJAQAAAADXH9wvI9IuBCBzgjWIT69gT6NrC0gszAQAAAAAAAAAAICWmAAAAAAAP3ZroHNmLwSTp35NiG7pKUCYB1yj44kEAAAAAAAAAAAALTEBAAAAAKXtw3rjhj8EBkeHDiOEL+csJ/DvybGLBAAAAAAAAAAAgJaYAAAAAACBtBVJ1UpBBIxNMaHUDR6P95xv/pfp8gSAlpgAAAAAAICWmAAAAAAAGOUVy9ziRgQgOctue+1YeoLJi30rNJcEAAAAAAAAAACAlpgAAAAAACnsaypEzkkEewZhe2Mya66+y0BZqdPXBAAAAAAAAAAAgMPJAQAAAAA5Vod80OJSBLqRRtamXqcxmVjjHwPbgAQAAAAAAAAAAAAtMQEAAAAAA/vSbxAOVAQOXWN+AA9Kce1UDC31NOUEgJaYAAAAAACAlpgAAAAAAO/88jFwxlYEwEi+tW0ykIo77+Y4ToboBICWmAAAAAAAgJaYAAAAAADtM4bOIdpWBGW1vyLTPtdwhEeli/C4xgQAAAAAAAAAAICWmAAAAAAAEy+mH6TgVwSp9WvCrWav0t4FvHpDBI8EgJaYAAAAAACAlpgAAAAAAO6dr7f3oV0EyXq6VWYqYEN0hJtR1mjMBAAAAAAAAAAAgJaYAAAAAABWPWbwkExgBHJ4+0NBxW26ijU3boU9/wQAAAAAAAAAAICWmAAAAAAAo8WEhy3sYQQsTzgSyRh/uW2uCgWZydEEAAAAAAAAAAAASX8PAAAAAO/kFlKltGQExU6LpkCFB7KE7WSOQUnSBAAAAAAAAAAAgJaYAAAAAAD/QooGXUpmBDCskYca6cck9vODZk1JxQSAlpgAAAAAAICWmAAAAAAA8Hu5Z+PaaATbhSynZfboXRDKksqFDNwEAAAAAAAAAAAALTEBAAAAAMZvnJqkwWkEREQbHue2svIj28/CptmmBICWmAAAAAAAgJaYAAAAAADNfp21Djd1BByPeoK+6K6feU3ZhiVeuASAlpgAAAAAAICWmAAAAAAA5Mlk+l6tdgRhQDeimQJ3ljDcPFOHLKkEAAAAAAAAAACAlpgAAAAAAP7xJMD303kEA+E2AtW5hX64CwFQYzzBBAAAAAAAAAAAgJaYAAAAAACZWOMfA9uABHJQTFSyRpE/OwCr44VdKgUAAAAAAAAAAAAtMQEAAAAAQJgHXKPjiQS1nWAIXv0241KgvssuelAFAAAAAAAAAAAALTEBAAAAACwn8O/JsYsEqSyYBfCpNptTlj+7tJdOBYCWmAAAAAAAgJaYAAAAAABr5YutXaCMBFaLaBqG/NZ6Vh8FCpouRwUAAAAAAAAAAICWmAAAAAAAgsmLfSs0lwSrovBMg9/ktt6fBpVNswwFAAAAAAAAAACAlpgAAAAAANgTx9AuHaYEYUA3opkCd5YHpZ+pdDIsBQAAAAAAAAAAgJaYAAAAAAAw3DxThyypBKtszRUpwr84Jxjx6co0UAWAlpgAAAAAAICWmAAAAAAAI/vYarTMrgQGR4cOI4Qv5+axdkk5i2EFAAAAAAAAAACAlpgAAAAAANpfKLLdR7YE5CgoAah7SHxbcCmlDWkxBYCWmAAAAAAAgMPJAQAAAAC4CwFQYzzBBMIiqcYvRxaMUcWVIC0JcAUAAAAAAAAAAICWmAAAAAAAhEeli/C4xgRWqCtd3eTNpK7NfuWyWS8FAAAAAAAAAACAlpgAAAAAAE+jawtILMwEhPFGL17zR+dlvMdeQY9zBQAAAAAAAAAAgJaYAAAAAAB0hJtR1mjMBMhEQut8+1ZbxBgtXz3CUQUAAAAAAAAAAICWmAAAAAAAba4KBZnJ0QTmWy3acgipcG3x2jOEy0YFAAAAAAAAAAAADicHAAAAAG2uCgWZydEEUL2517YSKW6MP2fnA0tGBQAAAAAAAAAAADtYCAAAAACE7WSOQUnSBFW3xIUQn91DyFcb+9jICgWAlpgAAAAAAICWmAAAAAAAvstAWanT1wQqUXvAN1+D8Eaf68pJckQFAAAAAAAAAACAlpgAAAAAAL7LQFmp09cEs56VfBe9kkBKAuJZOilSBQAAAAAAAAAAAC0xAQAAAABuIcEHYxjaBCYwRT5yf5yFiON7tlunfQUAAAAAAAAAAICWmAAAAAAAbiHBB2MY2gSmxguJyez6RIF+2NPWnwIFAAAAAAAAAACAlpgAAAAAAH2rZv052doEyXq6VWYqYEP+xBtrnZZ3BQAAAAAAAAAAAC0xAQAAAAB9q2b9OdnaBExQZR3YWZvuJSfOuUSebwUAAAAAAAAAAICWmAAAAAAAEMqSyoUM3ARj6Ba9dsbVafJM6kTbPn0FAAAAAAAAAAAALTEBAAAAAL6YwgBxIPAE2Vv2XRJJt6mFgHtK+bkoBYCWmAAAAAAAgJaYAAAAAADE++RAeMn1BNkjCXuTHSUD3fsFegLKJwUAAAAAAAAAAACHkwMAAAAAMHx7bJhI/AQB63+sE8rWFwc4ITql7yUFAAAAAAAAAAAAWmICAAAAADc5CgrbcvwE6EQpJMeiqXQcXrusJRl7BQAAAAAAAAAAgJaYAAAAAACKNTduhT3/BBmRAOzC/gLpqTeT/i/SPgWAlpgAAAAAAICWmAAAAAAAgX7Y09afAgWF/axaFAZ6W+i7v9oFscwFgJaYAAAAAACAlpgAAAAAAN6fBpVNswwF2lXP3hMDr7gy2m3L7x2KBQAAAAAAAAAAgJaYAAAAAAAHOCE6pe8lBQQNZU04QaxZfUPYeRhOhgUAAAAAAAAAAAAtMQEAAAAABzghOqXvJQVx/sW799BFUSak8+xc+cwFAAAAAAAAAACAlpgAAAAAAAc4ITql7yUFuw20YjmvCZL5kxdjsdKNBQAAAAAAAAAAgJaYAAAAAADd+wV6AsonBTfbAGvT61NQQQL/X7B3qgWAlpgAAAAAAICWmAAAAAAA3fsFegLKJwVPKRq5US5PKicGqGuAI/0FAAAAAAAAAACA8PoCAAAAADsAq+OFXSoF+2LyQFsp2IVyAqIF2YLmBQAAAAAAAAAAAC0xAQAAAAAHpZ+pdDIsBVpSzKpKDPMv8Ibi7ltVvgWAlpgAAAAAAICWmAAAAAAArs1+5bJZLwXYyas6zGuKCv7gNktp8ooFAAAAAAAAAACAlpgAAAAAAFtwKaUNaTEFXGZemHua7WJnnoQZtU6xBYCWmAAAAAAAAC0xAQAAAABGn+vKSXJEBS3jUMOau5IIFtq/SJA5jgWAlpgAAAAAAICWmAAAAAAAjD9n5wNLRgV8uSC3te6SNi2L4CWu8IAFAAAAAAAAAAAAO1gIAAAAAG3x2jOEy0YFAZMCTbZVcZ6o2+ApkurqBQAAAAAAAAAAAFpiAgAAAABt8dozhMtGBbsNtGI5rwmSJ2FSQ2NakAUAAAAAAAAAAIDw+gIAAAAAbfHaM4TLRgUZXs0RwXvVo2RxzR/iq8YFAAAAAAAAAACAw8kBAAAAAFYfBQqaLkcFFxW+8tl3mRE0m4GTY/vlBQAAAAAAAAAAgJaYAAAAAABSoL7LLnpQBbNQ7bG/O01jqq/e0wnFvgWAlpgAAAAAAICWmAAAAAAAUqC+yy56UAX0FXM049HETE0TJlPyIN8FAAAAAAAAAACAlpgAAAAAAMQYLV89wlEFO0IOrUg+IDfEem6Si/fsBQAAAAAAAAAAgJaYAAAAAABKAuJZOilSBaSWh0tCJk4pGbOh9rIcqAWAlpgAAAAAAAAtMQEAAAAA5rF2STmLYQWpLJgF8Kk2m7tRwpr7CLIFgJaYAAAAAACAlpgAAAAAACUnzrlEnm8F2Z+qnMnEx2F1o4E/kKmzBQAAAAAAAAAAgJaYAAAAAABRxZUgLQlwBf+mS68VFCQYZw0hmtGA5AUAAAAAAAAAAICWmAAAAAAAZbzHXkGPcwWhWYKFcVt4yo5kc3JqcIsFAAAAAAAAAACAlpgAAAAAAP7EG2udlncFyERC63z7Vls/Wg/k/JPVBQAAAAAAAAAAgJaYAAAAAAD+xBtrnZZ3BZaeCpT4G6CG8TdJPlpymwUAAAAAAAAAAICWmAAAAAAAHF67rCUZewUobxUzr1RcEb4RcNM49vIFAAAAAAAAAACAlpgAAAAAAPJM6kTbPn0FVqbWt4FhruUp+q12XvrRBQAAAAAAAAAAAC0xAQAAAACI43u2W6d9BXLF75xlFpmsM07fSTIm6AUAAAAAAAAAAICWmAAAAAAALYvgJa7wgAWN2MfAyI0MInv9owe7fToGAAAAAAAAAACAHSwEAAAAAC2L4CWu8IAFErlSuFmHsI4q0igvncRwBgAAAAAAAAAAgB0sBAAAAAB9Q9h5GE6GBdBbN4J9cKZLQWQigzR6MAYAAAAAAAAAAAAtMQEAAAAAMtpty+8digXhStFebo1tb1MfNpfDHTIGAAAAAAAAAACAlpgAAAAAAP7gNktp8ooFumK3BsgN4fztS0RXLskLBoCWmAAAAAAAgJaYAAAAAACOZHNyanCLBc+SV0aL2jv8FtsG7bBuYwaAlpgAAAAAAICWmAAAAAAA+ZMXY7HSjQV2vTskIsOsqu1PapgEbVEGAAAAAAAAAACAlpgAAAAAACdhUkNjWpAFeEQPvddLw1jrx72fwmJrBgAAAAAAAAAAAC0xAQAAAAAnYVJDY1qQBVIC0MlaomnKgvNXfxopKgYAAAAAAAAAAICWmAAAAAAAJ2FSQ2NakAV2vTskIsOsquDr25LEVR0GAAAAAAAAAAAALTEBAAAAAPE3ST5acpsFmhf2vZmWauuho0+FO68LBgAAAAAAAAAAgJaYAAAAAAAZs6H2shyoBTCskYca6cckSpaiBFv9EAaAlpgAAAAAAICWmAAAAAAAZ56EGbVOsQU9iQh4OK47o0wuLUd4bi4GgJaYAAAAAACAlpgAAAAAAHWjgT+QqbMFJqIagOdV/H1kcvkgA099BgAAAAAAAAAAgJaYAAAAAABkcc0f4qvGBTu2SyNbL4c28vpBmmLYRgYAAAAAAAAAAICWmAAAAAAAZHHNH+KrxgVRws6TIrggXPuNVN17dAMGgJaYAAAAAAAALTEBAAAAACak8+xc+cwFq3ew3RRr6JumCE8gR/psBoCWmAAAAAAAgJaYAAAAAAAp+q12XvrRBZRjYcmW6XhYQrpRNep+OwYAAAAAAAAAAICWmAAAAAAAKfqtdl760QUwXtmq5s6NugwGwGGVFm4GgJaYAAAAAACAlpgAAAAAAD9aD+T8k9UFy1epqhBpWu0oQPkUTMAOBgAAAAAAAAAAgJaYAAAAAABNEyZT8iDfBTKvgezXfoSWS9qWhXZNGwYAAAAAAAAAAICWmAAAAAAAZw0hmtGA5AXxye8Ad0mvUIeqUBGtXwcGgJaYAAAAAACAlpgAAAAAADSbgZNj++UF0nbloM8jK5Le6wQggeRFBoCWmAAAAAAAgJaYAAAAAAByAqIF2YLmBfh9uVZtcBB7KOTgXEVRXQYAAAAAAAAAAICWmAAAAAAAcgKiBdmC5gV4gDY3ZhGvEopqc+cxw2oGAAAAAAAAAACAlpgAAAAAADNO30kyJugFhuEJxDTCfl9P3ptIBk4tBgAAAAAAAAAAgJaYAAAAAACo2+ApkurqBcUegS5iNcwmj49C5gbgIgYAAAAAAAAAAABaYgIAAAAAxHpukov37AW9d0IHYXKPaY+/u/9rn24GgJaYAAAAAACAlpgAAAAAAL4RcNM49vIFVbnjk2mz8/TXpNugrVVrBoCWmAAAAAAAgJaYAAAAAAAnBqhrgCP9BaFZgoVxW3jK4EULPqsQYQaAlpgAAAAAAIDw+gIAAAAA+41U3Xt0AwZQrknHSjC9Sh7OSJiciMgGgJaYAAAAAACAlpgAAAAAAKGjT4U7rwsGySZnCW773vX1EC4WOa6dBgAAAAAAAAAAgJaYAAAAAAAoQPkUTMAOBgYmn4SxwWKvC+YX/2CQ4AaAlpgAAAAAAICWmAAAAAAAS9qWhXZNGwaBFpF5eWYl4Uox7XTfrtIGAAAAAAAAAACAlpgAAAAAAODr25LEVR0G1hmtUAzcCFP6aDt9MNvVBgAAAAAAAAAAAC0xAQAAAACPj0LmBuAiBt9HAeidKjSGAMufTL9zjQYAAAAAAAAAAICWmAAAAAAAj49C5gbgIgZc2dePof8bjxb9ZWFi/LsGAAAAAAAAAAAALTEBAAAAAI+PQuYG4CIGGay1VpKJ+LbyPCpvY3SRBgAAAAAAAAAAgJaYAAAAAACC81d/GikqBnND+IDPgrePvU0f1k11mQYAAAAAAAAAAICWmAAAAAAAT96bSAZOLQap9WvCrWav0pkTGHxkdeYGAAAAAAAAAACAlpgAAAAAAEFkIoM0ejAGGTyGyxPnTcSqvI4b3TylBgAAAAAAAAAAAC0xAQAAAABTHzaXwx0yBkREGx7ntrLyipvBJ7TTmgaAlpgAAAAAAICWmAAAAAAAe/2jB7t9OgYm91kTY/1UDoEKJmLZ4pUGAAAAAAAAAAAAh5MDAAAAAHv9owe7fToGjOMJdLrlV5+os7KGotbRBgAAAAAAAAAAgJaYAAAAAABCulE16n47Bmz4L5IsNeDPqXtqeb/gtwYAAAAAAAAAAICWmAAAAAAA8vpBmmLYRgaRY2O3J4YCacs31CuUQMUGAAAAAAAAAACAlpgAAAAAAO1PapgEbVEGRt8BGLYTcU1m6WbNX7GMBgAAAAAAAAAAgJaYAAAAAAAo5OBcRVFdBktXHpAB3jx6NQbvsEf7uQYAAAAAAAAAAICWmAAAAAAA4EULPqsQYQbPkldGi9o7/NemtuGJUIwGgMPJAQAAAACAw8kBAAAAAOBFCz6rEGEG+R8CyJVDuxwBqAMnDBaNBoCWmAAAAAAAgJaYAAAAAACKanPnMcNqBqXte8Z1guYAtvlSjlGx7wYAAAAAAAAAAICWmAAAAAAA68e9n8JiawYznk7U9cAkyaeDOmVCRJMGAAAAAAAAAAAALTEBAAAAACrSKC+dxHAGfmAFANfFpdI9O3ZjN8fRBgAAAAAAAAAAgPD6AgAAAAAq0igvncRwBozjCXS65Vef0PRYQDMg9gYAAAAAAAAAAAAtMQEAAAAAZHL5IANPfQboz+HnnCC38YoEc9K9SdgGAAAAAAAAAACAlpgAAAAAAGbpZs1fsYwGyDqbgGXGpgHpNoy/NbULBwAAAAAAAAAAgJaYAAAAAAAAy59Mv3ONBlqA4pT59JarE8ju2J1kDQcAAAAAAAAAAICWmAAAAAAA8jwqb2N0kQZ2vTskIsOsqoaIzY/ikUwHAAAAAAAAAACAlpgAAAAAAKeDOmVCRJMGc9TFdLUrT2pgMLH6RBx6BwAAAAAAAAAAAC0xAQAAAACBCiZi2eKVBpcMX2ZSavfA1Pk/zv69fgcAAAAAAAAAAABaYgIAAAAAgQomYtnilQYdDypgLvNXoPJ3zkV5yR4HAAAAAAAAAAAALTEBAAAAAL1NH9ZNdZkG2BvtuxTXtNhZHrcUxRoZBwAAAAAAAAAAgJaYAAAAAAD1EC4WOa6dBj7FihbCxxnQvOgUegwyHweAlpgAAAAAAICWmAAAAAAAqryOG908pQbxlGTpwn5zSif8/lE0K3oHAAAAAAAAAAAALTEBAAAAAKl7anm/4LcGB3YjQuZFJMaPV3orTTQJBwAAAAAAAAAAgJaYAAAAAAA1Bu+wR/u5BrjK/kB+2befecoPp8bmUgcAAAAAAAAAAICWmAAAAAAAFv1lYWL8uwYPxCWC05nVxx4OkS6M5U8HAAAAAAAAAAAALTEBAAAAAMs31CuUQMUGzqb8+xpEgotRa7jUw25dBwAAAAAAAAAAgJaYAAAAAAA9O3ZjN8fRBpcMX2ZSavfAYlnv4ngZcQcAAAAAAAAAAAAtMQEAAAAAPTt2YzfH0QYdDypgLvNXoFaiTZWfYR0HAAAAAAAAAAAALTEBAAAAAD07dmM3x9EGpsYLicns+kQcVFBKUbt7BwAAAAAAAAAAgJaYAAAAAACos7KGotbRBnvWVutQqS9u5fxkfGO4eAcAAAAAAAAAAICWmAAAAAAASjHtdN+u0gaF/axaFAZ6Wy6EIaiDpW8HgJaYAAAAAACAlpgAAAAAAPpoO30w29UGbjWQFlwRN6zfoX3X+ywlBwAAAAAAAAAAAC0xAQAAAACKBHPSvUnYBmtR4D8YRs0U9Q2/FqHuAQcAAAAAAAAAAICWmAAAAAAAmRMYfGR15gZyePtDQcVtumCtX9GjX0wHAAAAAAAAAACAlpgAAAAAALb5Uo5Rse8GsGQ8iLkxdzpHe4+n7EAFBwAAAAAAAAAAgJaYAAAAAADQ9FhAMyD2BnvWVutQqS9uuKVVy6XzNAcAAAAAAAAAAICWmAAAAAAA0PRYQDMg9gamXne7AnvwpYyqkAkpeR4HgJaYAAAAAACAlpgAAAAAAPUNvxah7gEHykbbjAfwBmNJaYDiSxG5BwAAAAAAAAAAgJaYAAAAAABHe4+n7EAFB1pQU4TQ5kGuSAzKfDr8jQcAAAAAAAAAAICWmAAAAAAAj1d6K000CQc9nFjYahDyUzivM7dClv4HgJaYAAAAAACAlpgAAAAAAOk2jL81tQsH2fFB8HNqFi9XoYq6hlSnBwAAAAAAAAAAgJaYAAAAAAATyO7YnWQNBx6DBGTYyJBZxk4j6sTj3AcAAAAAAAAAAICWmAAAAAAAWR63FMUaGQcmu+Fgoc5gECDUG9tpOdcHgJaYAAAAAACAlpgAAAAAAFaiTZWfYR0HP0fq7kOdRwvAPA5Mm8uuB4CWmAAAAAAAgJaYAAAAAABWok2Vn2EdB+hEKSTHoql0dxVJp+jj1QeAlpgAAAAAAICWmAAAAAAA8nfORXnJHgeG4QnENMJ+XzNDMQbjtJkHAAAAAAAAAACAlpgAAAAAAPJ3zkV5yR4HdVR0nCKcpv/iPQfzXO24BwAAAAAAAAAAgJaYAAAAAADfoX3X+ywlBza1URQD0cxILewDlxnh+QeAlpgAAAAAAAAtMQEAAAAAuKVVy6XzNAfLRfYfu5jNbmEC2khWXt0HAAAAAAAAAACAlpgAAAAAAGCtX9GjX0wHqxm23pcDK91imYvMPkziB4CWmAAAAAAAgJaYAAAAAACGiM2P4pFMB0bfARi2E3FNzPKxlrcn1QcAAAAAAAAAAICWmAAAAAAAHg6RLozlTwfZ7uKotOy2QbyByE/jXJEHAAAAAAAAAAAALTEBAAAAAHnKD6fG5lIH2EtV7j/ZrJ4Lq7kqx4CTBwAAAAAAAAAAgJaYAAAAAABRa7jUw25dB6bGC4nJ7PpEIrxx0JddigcAAAAAAAAAAICWmAAAAAAAYlnv4ngZcQfxt7K5GFSQKYO7vrbb4YMHAAAAAAAAAAAALTEBAAAAAOX8ZHxjuHgHV9tYsGP+vWoXg+yZvBCIBwAAAAAAAAAAgJaYAAAAAABgMLH6RBx6B1VdBBt17qJnyQMDFRHW/AcAAAAAAAAAAAAtMQEAAAAAJ/z+UTQregctib/4Kmh83/ynNYFgwpoHAAAAAAAAAAAALTEBAAAAABxUUEpRu3sHc1P6Ox14wvvuLSAMgrPLB4CWmAAAAAAAgJaYAAAAAADU+T/O/r1+B6bGC4nJ7PpEYJrT6XGNjgcAAAAAAAAAAICWmAAAAAAA1Pk/zv69fgfxt7K5GFSQKTvUQtva7/gHAAAAAAAAAACAw8kBAAAAAIO7vrbb4YMHXaVARkiI/d5dWVj+kCtZCAAAAAAAAAAAAC0xAQAAAAAXg+yZvBCIB0wN+I2qrTS/u7vfEjwELQiAlpgAAAAAAICWmAAAAAAAIrxx0Jddigep9WvCrWav0sOB6GTGSXIIgJaYAAAAAACAlpgAAAAAAEgMynw6/I0H6JRPUgi9otwBXOELmLMBCAAAAAAAAAAAgJaYAAAAAABgmtPpcY2OB2XxYUZ6vRos7LLUDVqVcgiAlpgAAAAAAICWmAAAAAAAvIHIT+NckQcMIJIkJ+C6MAXDgI/MZDAIAAAAAAAAAAAALTEBAAAAAAuruSrHgJMHBkeHDiOEL+chX9O2MlpxCAAAAAAAAAAAgJaYAAAAAAAzQzEG47SZB6n1a8KtZq/SDuyqaGIwbggAAAAAAAAAAICWmAAAAAAA/Kc1gWDCmgda6sJ3kzMCPMdSywgrPngIAAAAAAAAAAAALTEBAAAAAFehirqGVKcHjF3Gg+ar5IIAIwDLO/kPCAAAAAAAAAAAgJaYAAAAAADiPQfzXO24BzioR/CEEfUAMVW5Fx+jTQgAAAAAAAAAAICWmAAAAAAASWmA4ksRuQeR8VJ+SpP9zYPUOlHI3VcIAAAAAAAAAACAlpgAAAAAAMzysZa3J9UHyDqbgGXGpgGD6kcaTH58CAAAAAAAAAAAgJaYAAAAAADGTiPqxOPcB2OP1HtkwmPtjU/2nbn+cAgAAAAAAAAAAICWmAAAAAAAYQLaSFZe3Qf4hyrG2t9TY24QNo4+sTcIAAAAAAAAAACAlpgAAAAAADvUQtva7/gHXaVARkiI/d4gBNldSMcvCAAAAAAAAAAAgMPJAQAAAAAt7AOXGeH5B8JAbvbcb1HqSDaTkVstSAiAlpgAAAAAAICWmAAAAAAAyQMDFRHW/AeIL4qJQeItszCMlQzQFH8IAAAAAAAAAACAlpgAAAAAAMkDAxUR1vwHG3akRvjvQv5ykc10WbonCAAAAAAAAAAAgJaYAAAAAAABXOELmLMBCO7HASIibT0qJc+vzNz6jggAAAAAAAAAAICWmAAAAAAAACMAyzv5DwiuSdmPZWDEF7l3U/AcZ7MIAAAAAAAAAACAlpgAAAAAAHKRzXRZuicIeL1nYvwGdcyNxQRnyjD5CAAAAAAAAAAAgJaYAAAAAAAgBNldSMcvCF0ka3od9QIm7qJ+TYfGxQgAAAAAAAAAAIDDyQEAAAAABcOAj8xkMAgzmGHnJbPurL990B5aGsQIAAAAAAAAAAAALTEBAAAAAG4QNo4+sTcInGLTzgXdM7x5TJ43sTP/CAAAAAAAAAAAgJaYAAAAAAAxVbkXH6NNCLc0fIVDsTpBjrEmxuAV4giAlpgAAAAAAICWmAAAAAAAg9Q6UcjdVwilYkFtlSd0BLrhqncWe4sIAAAAAAAAAACAlpgAAAAAAF1ZWP6QK1kIXSRreh31AiblkNwV3tP6CAAAAAAAAAAAgJaYAAAAAABdWVj+kCtZCO3DzQ9nQHglI3wweIeiuggAAAAAAAAAAICWmAAAAAAADuyqaGIwbghyePtDQcVtulMNSUaeTqYIAAAAAAAAAACAlpgAAAAAAI1P9p25/nAIucbmosGJS8DgWdoxdqCsCAAAAAAAAAAAgJaYAAAAAAAhX9O2MlpxCKksmAXwqTab0wvcDLzNhgiAlpgAAAAAAICWmAAAAAAAx1LLCCs+eAha6sJ3kzMCPFxStvg6SKsIAAAAAAAAAAAALTEBAAAAAIPqRxpMfnwI2fFB8HNqFi9mcW/rnG6KCAAAAAAAAAAAgJaYAAAAAAAwjJUM0BR/CJTPAw1cwBod4hdMJmuZxggAAAAAAAAAAICWmAAAAAAAZnFv65xuigiMXcaD5qvkgnOhEnC/53cJAAAAAAAAAACAlpgAAAAAALrhqncWe4sI9QGm631Xjqw+fJQPC6ROCYCWmAAAAAAAgJaYAAAAAAAlz6/M3PqOCKksmAXwqTabyo94hQfqZwmAlpgAAAAAAICWmAAAAAAAUw1JRp5OpggZkQDswv4C6Q0PTfwDVigJgJaYAAAAAACAlpgAAAAAAFxStvg6SKsIfUkhFyAkdXjdO7vEaQoSCQAAAAAAAAAAAC0xAQAAAADgWdoxdqCsCGB9R0FRbnq4G7pR/kqCTAmAlpgAAAAAAICWmAAAAAAAuXdT8BxnswgdF+dCM5L6M/mafjRWQBEJAAAAAAAAAACAlpgAAAAAACN8MHiHoroI43DMjxLDab9/5oNxTHkfCQAAAAAAAAAAgJaYAAAAAAC/fdAeWhrECPrQMu9jn1hzSuUMUg23KgkAAAAAAAAAAAAtMQEAAAAA7qJ+TYfGxQgCbbOVbwmfT64C8Zx7Jz4JAAAAAAAAAACAlpgAAAAAAO6ifk2HxsUITatWzfpiEzelrPjtb1tZCQAAAAAAAAAAgJaYAAAAAADuon5Nh8bFCLbGpgio6cBe7i68yttoXAmAlpgAAAAAAICWmAAAAAAA4hdMJmuZxgjlneoZxDVSvPTfbQHnjnkJAAAAAAAAAACAlpgAAAAAAI3FBGfKMPkIUiU9oObHTsIyCUJIgJJoCQAAAAAAAAAAgJaYAAAAAADlkNwV3tP6CBd6pQa4AoN++xA9XjB3DwkAAAAAAAAAAICWmAAAAAAAeUyeN7Ez/wjDe/qdiafTsQ7oedDeWg8JAAAAAAAAAACAlpgAAAAAAA7oedDeWg8JYHd1wACuCRAgPS9SATKjCYCWmAAAAAAAgJaYAAAAAAD7ED1eMHcPCVZfiVE5t4IHz994ND4vuQmAlpgAAAAAAICWmAAAAAAA+Zp+NFZAEQkGR4cOI4Qv5//6ess+J/EJAAAAAAAAAACAlpgAAAAAAN07u8RpChIJzqb8+xpEgotfskOUQXKvCQAAAAAAAAAAgJaYAAAAAADdO7vEaQoSCZAxV24m2wtjacV3zwTa+QmAlpgAAAAAAICWmAAAAAAAf+aDcUx5HwnWMBrRB2IFQsszQIxSm7sJAAAAAAAAAACAlpgAAAAAAErlDFINtyoJjxk8xF6hp9WE2AqqFHD5CQAAAAAAAAAAAC0xAQAAAACuAvGceyc+CXEbU2pgWvDyVudIbt5t0QkAAAAAAAAAAICWmAAAAAAApaz47W9bWQlCm86ucQLlUsWHKzN8eN4JAAAAAAAAAACAlpgAAAAAADIJQkiAkmgJGy3OLRhB4VTVxoOGq2yaCYCWmAAAAAAAgJaYAAAAAABzoRJwv+d3Ca5J2Y9lYMQXnpfj7dtxnQkAAAAAAAAAAICWmAAAAAAA9N9tAeeOeQnuIqTY5B+6g87xLKOahcAJAAAAAAAAAACAlpgAAAAAAJ6X4+3bcZ0JHRfnQjOS+jOEOZVPoYRUCgAAAAAAAAAAgJaYAAAAAABfskOUQXKvCabGC4nJ7PpEc4h62wJXWAoAAAAAAAAAAICWmAAAAAAAyzNAjFKbuwnseL9Vb6p3J4q5ZKmnSnwKAAAAAAAAAACAlpgAAAAAAM7xLKOahcAJpsYLicns+kRtle4SHixiCgAAAAAAAAAAgJaYAAAAAABW50hu3m3RCcMD/mcNHsdfYHjryUqjYgqAlpgAAAAAAICWmAAAAAAAxYcrM3x43gmgu/uRBNEbfpSMbHBcEgoKAAAAAAAAAACAlpgAAAAAAP/6ess+J/EJqSyYBfCpNpvgfMCX1plsCoCWmAAAAAAAgJaYAAAAAACE2AqqFHD5CVx89Mkpx7K13nuM5rC9EgoAAAAAAAAAAAAtMQEAAAAAlIxscFwSCgoVFqboGj/c+0xmb7JCGeoKgJaYAAAAAACAlpgAAAAAAN57jOawvRIKaj4O5cDsUyD7/a3GZ1LDCgAAAAAAAAAAAC0xAQAAAACEOZVPoYRUCgZHhw4jhC/nYMZt+UiC8QoAAAAAAAAAAICWmAAAAAAAc4h62wJXWAqF/axaFAZ6W3mJRdZONKMKgJaYAAAAAACAlpgAAAAAAG2V7hIeLGIKqfVrwq1mr9IuGw+G5M3TCgAAAAAAAAAAgJaYAAAAAACKuWSpp0p8CmB3dcAArgkQRue65APeogqAlpgAAAAAAICWmAAAAAAA+/2txmdSwwqNPs/A0OxGUz2typdCL1ELAAAAAAAAAAAALTEBAAAAAC4bD4bkzdMKIdn4eG6LA43ziNnpNLs1CwAAAAAAAAAAgJaYAAAAAABgxm35SILxCqksmAXwqTabPB4Le1LzNQuAlpgAAAAAAICWmAAAAAAA84jZ6TS7NQvRDe6lG4IOcx1rZOEtqfkLAAAAAAAAAACAlpgAAAAAAD2typdCL1ELHRfnQjOS+jONXqrwfOPpCwAAAAAAAAAAAC0xAQAAAACNXqrwfOPpCwZHhw4jhC/nOoQurcLESAwAAAAAAAAAAICWmAAAAAAAjV6q8Hzj6QuzO1wJckN8mYSkPqjdmwgMAAAAAAAAAACAlpgAAAAAAB1rZOEtqfkLSwq//TTYVGecC0jLjVRkDAAAAAAAAAAAgJaYAAAAAACEpD6o3ZsIDCHsDLRqK45no8ZHlNQwngwAAAAAAAAAAICWmAAAAAAAOoQurcLESAypLJgF8Kk2m69MGUaItqwMgJaYAAAAAACAlpgAAAAAAJwLSMuNVGQMLVC+2K79KVee8qmmeQ7iDAAAAAAAAAAAgJaYAAAAAACjxkeU1DCeDGtR4D8YRs0UgrLQ9oMwdg0AAAAAAAAAAICWmAAAAAAAnvKppnkO4gzZW/ZdEkm3qeSVY6D3Y04NAAAAAAAAAACAlpgAAAAAAOSVY6D3Y04Na1HgPxhGzRTKQbMDLwCuDQAAAAAAAAAAgJaYAAAAAACCstD2gzB2DcR0RkzCbw+YdIUBQITI/Q0AAAAAAAAAAICWmAAAAAAAykGzAy8Arg0EM7yyG4LxrjzVpgU8jj8OAAAAAAAAAACAlpgAAAAAAHSFAUCEyP0NsScJFvdqzBbH1iAajbB3DgAAAAAAAAAAgJaYAAAAAAA81aYFPI4/DrYigPE2MxwWfKVVtTAr4g4AAAAAAAAAAICWmAAAAAAAx9YgGo2wdw44emcnFFCj1bkACCGc87QOAAAAAAAAAACAlpgAAAAAALkACCGc87QOumK3BsgN4fxuYB3sspdYD4CWmAAAAAAAgJaYAAAAAAB8pVW1MCviDjWYAyfJzYYSMrFGtIQPJQ+AlpgAAAAAAICWmAAAAAAA"; - let b64_resp = "CgV0b3RhbAo2Z2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby4oKlNlc3Npb24pLlN0YXJ0LmZ1bmMxCjlnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9zY3JhcGUuKCpzY3JhcGVMb29wKS5ydW4KD3J1bnRpbWUuYmdzd2VlcApeZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyLigqcHlyb3Njb3BlUmVjZWl2ZXIpLmhhbmRsZS5mdW5jMQoNcnVudGltZS5tY2FsbAohbmV0L2h0dHAuKCpUcmFuc3BvcnQpLmRpYWxDb25uRm9yCltnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9pbnRlcm5hbC9tZW1vcnlsaW1pdGVyLigqTWVtb3J5TGltaXRlcikuc3RhcnRNb25pdG9yaW5nLmZ1bmMxCjxnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9kaXNjb3ZlcnkuKCpNYW5hZ2VyKS5zZW5kZXIKFm5ldC9odHRwLigqY29ubikuc2VydmUKIW5ldC9odHRwLigqcGVyc2lzdENvbm4pLndyaXRlTG9vcAosbmV0LigqUmVzb2x2ZXIpLmdvTG9va3VwSVBDTkFNRU9yZGVyLmZ1bmMzLjEKFnJ1bnRpbWUuZ2NCZ01hcmtXb3JrZXIKSWdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3Byb2Nlc3Nvci9iYXRjaHByb2Nlc3Nvci4oKnNoYXJkKS5zdGFydExvb3AKEXJ1bnRpbWUubmFub3RpbWUxCkRnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL3Vwc3RyZWFtL3JlbW90ZS4oKlJlbW90ZSkuaGFuZGxlSm9icwqWAWdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2V4cG9ydGVyL2ludGVybmFsL3F1ZXVlLigqQ29uc3VtZXJzW2dvLnNoYXBlLmludGVyZmFjZSB7IEV4cG9ydChjb250ZXh0LkNvbnRleHQpIGVycm9yOyBJdGVtc0NvdW50KCkgaW50IH1dKS5TdGFydC5mdW5jMQolaW50ZXJuYWwvc2luZ2xlZmxpZ2h0LigqR3JvdXApLmRvQ2FsbAo4Z2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby4oKlNlc3Npb24pLnRha2VTbmFwc2hvdHMKRWdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS4oKnNjcmFwZUxvb3ApLnNjcmFwZUFuZFJlcG9ydAoQcnVudGltZS5zd2VlcG9uZQpeZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyLigqcHlyb3Njb3BlUmVjZWl2ZXIpLnJlYWRQcm9maWxlcwoOcnVudGltZS5wYXJrX20KEXJ1bnRpbWUuZ29zY2hlZF9tCg9ydW50aW1lLmdvZXhpdDAKJW5ldC9odHRwLigqVHJhbnNwb3J0KS5kZWNDb25uc1Blckhvc3QKHm5ldC9odHRwLigqVHJhbnNwb3J0KS5kaWFsQ29ubgpUZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvaW50ZXJuYWwvbWVtb3J5bGltaXRlci4oKk1lbW9yeUxpbWl0ZXIpLkNoZWNrTWVtTGltaXRzChBydW50aW1lLnNlbGVjdGdvCiJuZXQvaHR0cC4oKnJlc3BvbnNlKS5maW5pc2hSZXF1ZXN0ChVidWZpby4oKldyaXRlcikuRmx1c2gKGW5ldC9odHRwLigqUmVxdWVzdCkud3JpdGUKGm5ldC4oKlJlc29sdmVyKS50cnlPbmVOYW1lChJydW50aW1lLmdjTWFya0RvbmUKE3J1bnRpbWUuc3lzdGVtc3RhY2sKS2dvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3Byb2Nlc3Nvci9iYXRjaHByb2Nlc3Nvci4oKnNoYXJkKS5wcm9jZXNzSXRlbQpEZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby91cHN0cmVhbS9yZW1vdGUuKCpSZW1vdGUpLnNhZmVVcGxvYWQKmwFnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9pbnRlcm5hbC9xdWV1ZS4oKmJvdW5kZWRNZW1vcnlRdWV1ZVtnby5zaGFwZS5pbnRlcmZhY2UgeyBFeHBvcnQoY29udGV4dC5Db250ZXh0KSBlcnJvcjsgSXRlbXNDb3VudCgpIGludCB9XSkuQ29uc3VtZQoibmV0LigqUmVzb2x2ZXIpLmxvb2t1cElQQWRkci5mdW5jMQowZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby4oKlNlc3Npb24pLnJlc2V0CktnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9zY3JhcGUuKCpzY3JhcGVMb29wKS5zY3JhcGVBbmRSZXBvcnQuZnVuYzEKB2lvLkNvcHkKFGNvbnRleHQuV2l0aERlYWRsaW5lChVuZXQvaHR0cC4oKkNsaWVudCkuRG8KS2dpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS4oKnNjcmFwZUxvb3ApLnNjcmFwZUFuZFJlcG9ydC5mdW5jMgo8Z2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvc2NyYXBlLigqc2NyYXBlTG9vcCkuYXBwZW5kChxydW50aW1lLigqc3dlZXBMb2NrZWQpLnN3ZWVwCiFydW50aW1lLigqc3dlZXBMb2NrZXIpLnRyeUFjcXVpcmUKHGVuY29kaW5nL2JpbmFyeS5BcHBlbmRWYXJpbnQKX2dpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9yZWNlaXZlci9weXJvc2NvcGVyZWNlaXZlci4oKnB5cm9zY29wZVJlY2VpdmVyKS5vcGVuTXVsdGlwYXJ0Ch1lbmNvZGluZy9iaW5hcnkuQXBwZW5kVXZhcmludApdZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyL3Bwcm9mcGFyc2VyLigqcFByb2ZQYXJzZXIpLlBhcnNlCmBnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvcmVjZWl2ZXIvcHlyb3Njb3BlcmVjZWl2ZXIvY29tcHJlc3MuKCpEZWNvbXByZXNzb3IpLkRlY29tcHJlc3MKTGdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9yZWNlaXZlci9weXJvc2NvcGVyZWNlaXZlci5wb3N0UHJvY2Vzc1Byb2YKEHJ1bnRpbWUuc2NoZWR1bGUKE3J1bnRpbWUuZ29zY2hlZEltcGwKEHJ1bnRpbWUuZ2Rlc3Ryb3kKGm5ldC9odHRwLigqVHJhbnNwb3J0KS5kaWFsClJnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9pbnRlcm5hbC9tZW1vcnlsaW1pdGVyLigqTWVtb3J5TGltaXRlcikucmVhZE1lbVN0YXRzCg9ydW50aW1lLnNlbGxvY2sKEXJ1bnRpbWUuc2VsdW5sb2NrCiBuZXQvaHR0cC5wZXJzaXN0Q29ubldyaXRlci5Xcml0ZQoabmV0L2h0dHAubmV3VHJhbnNmZXJXcml0ZXIKGG5ldC4oKlJlc29sdmVyKS5leGNoYW5nZQoZcnVudGltZS5nY01hcmtUZXJtaW5hdGlvbgoicnVudGltZS5nY0RyYWluTWFya1dvcmtlckRlZGljYXRlZAodcnVudGltZS5nY0RyYWluTWFya1dvcmtlcklkbGUKUGdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3BkYXRhL3BtZXRyaWMuUmVzb3VyY2VNZXRyaWNzU2xpY2UuTW92ZUFuZEFwcGVuZFRvCkdnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL3Vwc3RyZWFtL3JlbW90ZS4oKlJlbW90ZSkudXBsb2FkUHJvZmlsZQpKZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvZXhwb3J0ZXIvZXhwb3J0ZXJoZWxwZXIubmV3UXVldWVTZW5kZXIuZnVuYzEKDm5ldC5pbml0LmZ1bmMxChRydW50aW1lLmFjcXVpcmVTdWRvZwo1Z2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby4oKlNlc3Npb24pLnVwbG9hZERhdGEKT2dvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3JlY2VpdmVyL3JlY2VpdmVyaGVscGVyLigqT2JzUmVwb3J0KS5FbmRNZXRyaWNzT3AKdGdpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9yZWNlaXZlci9wcm9tZXRoZXVzcmVjZWl2ZXIvaW50ZXJuYWwuKCp0cmFuc2FjdGlvbikuQ29tbWl0Cg1pby5jb3B5QnVmZmVyChljb250ZXh0LldpdGhEZWFkbGluZUNhdXNlChVuZXQvaHR0cC4oKkNsaWVudCkuZG8KPGdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS4oKnNjcmFwZUxvb3ApLnJlcG9ydApGZ2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvbW9kZWwvbGFiZWxzLigqU2NyYXRjaEJ1aWxkZXIpLkxhYmVscwpIZ2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvbW9kZWwvdGV4dHBhcnNlLigqUHJvbVBhcnNlcikubmV4dFRva2VuCkNnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9zY3JhcGUuKCpzY3JhcGVDYWNoZSkuZm9yRWFjaFN0YWxlCj1naXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9tb2RlbC9sYWJlbHMuTGFiZWxzLlZhbGlkYXRlCj1naXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9zY3JhcGUubmV3U2NyYXBlUG9vbC5mdW5jMS4xCkNnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9zY3JhcGUuKCp0aW1lTGltaXRBcHBlbmRlcikuQXBwZW5kCkVnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9tb2RlbC90ZXh0cGFyc2UuKCpQcm9tUGFyc2VyKS5NZXRyaWMKH3J1bnRpbWUuKCpnY0JpdHNBcmVuYSkudHJ5QWxsb2MKFHJ1bnRpbWUubG9ja1dpdGhSYW5rCiZuZXQvaHR0cC4oKlJlcXVlc3QpLlBhcnNlTXVsdGlwYXJ0Rm9ybQoRcnVudGltZS5ncm93c2xpY2UKJWdpdGh1Yi5jb20vZ29vZ2xlL3Bwcm9mL3Byb2ZpbGUuUGFyc2UKPGdpdGh1Yi5jb20vZ29vZ2xlL3Bwcm9mL3Byb2ZpbGUuKCpQcm9maWxlKS5Xcml0ZVVuY29tcHJlc3NlZApfZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL3JlY2VpdmVyL3B5cm9zY29wZXJlY2VpdmVyL2NvbXByZXNzLigqRGVjb21wcmVzc29yKS5yZWFkQnl0ZXMKRmdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9yZWNlaXZlci9weXJvc2NvcGVyZWNlaXZlci5nZXROb2RlSWQKJWdpdGh1Yi5jb20vZ28tZmFzdGVyL2NpdHkuaGFzaDEyOHRvNjQKCnNvcnQuU2xpY2UKGHJ1bnRpbWUubWFwYXNzaWduX2Zhc3Q2NAoWcnVudGltZS51bmxvY2tXaXRoUmFuawoVcnVudGltZS5yZXNldHNwaW5uaW5nChRydW50aW1lLmZpbmRSdW5uYWJsZQoNcnVudGltZS53YWtlcAoQcnVudGltZS5mdW5jbmFtZQo4Z2l0aHViLmNvbS9td2l0a293L2dvLWNvbm50cmFjay5OZXdEaWFsQ29udGV4dEZ1bmMuZnVuYzEKEXJ1bnRpbWUubmV3b2JqZWN0ChRydW50aW1lLlJlYWRNZW1TdGF0cwojbmV0L2h0dHAuY2hlY2tDb25uRXJyb3JXcml0ZXIuV3JpdGUKEW5ldC4oKmNvbm4pLldyaXRlCh5uZXQvaHR0cC5pc0tub3duSW5NZW1vcnlSZWFkZXIKFG5ldC4oKlJlc29sdmVyKS5kaWFsChZuZXQuZG5zUGFja2V0Um91bmRUcmlwChFuZXQuKCpjb25uKS5DbG9zZQoTcnVudGltZS5tUHJvZl9GbHVzaAoPcnVudGltZS5nY0RyYWluCk9nby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci4oKmJhc2VSZXF1ZXN0U2VuZGVyKS5zZW5kChhuZXQuKCpSZXNvbHZlcikubG9va3VwSVAKOmdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28uKCpTZXNzaW9uKS5kdW1wSGVhcFByb2ZpbGUKJWdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28ubnVtR0MKSGdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3JlY2VpdmVyL3JlY2VpdmVyaGVscGVyLigqT2JzUmVwb3J0KS5lbmRPcApXZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvaW50ZXJuYWwvZmFub3V0Y29uc3VtZXIuKCptZXRyaWNzQ29uc3VtZXIpLkNvbnN1bWVNZXRyaWNzCnhnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcmVjZWl2ZXIvcHJvbWV0aGV1c3JlY2VpdmVyL2ludGVybmFsLigqdHJhbnNhY3Rpb24pLmdldE1ldHJpY3MKhAFnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcmVjZWl2ZXIvcHJvbWV0aGV1c3JlY2VpdmVyL2ludGVybmFsLigqaW5pdGlhbFBvaW50QWRqdXN0ZXIpLkFkanVzdE1ldHJpY3MKGGJ5dGVzLigqQnVmZmVyKS5SZWFkRnJvbQokY29udGV4dC4oKmNhbmNlbEN0eCkucHJvcGFnYXRlQ2FuY2VsChduZXQvaHR0cC4oKkNsaWVudCkuc2VuZApFZ2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvc2NyYXBlLigqc2NyYXBlTG9vcCkuYWRkUmVwb3J0U2FtcGxlCgtydW50aW1lLmFkZAo1Z2l0aHViLmNvbS9wcm9tZXRoZXVzL2NvbW1vbi9tb2RlbC5MYWJlbFZhbHVlLklzVmFsaWQKOGdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL21vZGVsL2xhYmVscy5OZXdCdWlsZGVyCnpnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcmVjZWl2ZXIvcHJvbWV0aGV1c3JlY2VpdmVyL2ludGVybmFsLigqdHJhbnNhY3Rpb24pLmdldFNlcmllc1JlZgoRc3RyaW5ncy5JbmRleEJ5dGUKDXJ1bnRpbWUubG9jazIKHW5ldC9odHRwLigqUmVxdWVzdCkuUGFyc2VGb3JtChBydW50aW1lLm1hbGxvY2djCilnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLlBhcnNlRGF0YQoKaW8uUmVhZEFsbAonZ2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS5tYXJzaGFsCh5naXRodWIuY29tL2dvLWZhc3Rlci9jaXR5LkNINjQKEXNvcnQucGRxc29ydF9mdW5jChFydW50aW1lLm1lbWhhc2g2NAoPcnVudGltZS51bmxvY2syChpydW50aW1lLigqcmFuZG9tRW51bSkubmV4dAoxcnVudGltZS4oKmdjQ29udHJvbGxlclN0YXRlKS5maW5kUnVubmFibGVHQ1dvcmtlcgoNcnVudGltZS5zdG9wbQoNcnVudGltZS5tUGFyawoRcnVudGltZS5zdGVhbFdvcmsKEHJ1bnRpbWUubmFub3RpbWUKD3J1bnRpbWUubmV0cG9sbAoOcnVudGltZS5zdGFydG0KD3J1bnRpbWUuZXhlY3V0ZQoWcnVudGltZS5nb3N0cmluZ25vY29weQoRcnVudGltZS5wTWFzay5zZXQKNWdpdGh1Yi5jb20vbXdpdGtvdy9nby1jb25udHJhY2suZGlhbENsaWVudENvbm5UcmFja2VyChVydW50aW1lLnN0YXJ0VGhlV29ybGQKFHJ1bnRpbWUuc3RvcFRoZVdvcmxkChJuZXQuKCpuZXRGRCkuV3JpdGUKEnJ1bnRpbWUudHlwZUFzc2VydAoZbmV0LigqRGlhbGVyKS5EaWFsQ29udGV4dAoSbmV0LigqbmV0RkQpLkNsb3NlChlydW50aW1lLm1Qcm9mX0ZsdXNoTG9ja2VkChlydW50aW1lLigqZ2NXb3JrKS5iYWxhbmNlChBydW50aW1lLm1hcmtyb290ChdydW50aW1lLmdjRmx1c2hCZ0NyZWRpdAodcnVudGltZS50eXBlUG9pbnRlcnMubmV4dEZhc3QKEnJ1bnRpbWUuYXJlbmFJbmRleAoScnVudGltZS5zY2Fub2JqZWN0ChJydW50aW1lLndiQnVmRmx1c2gKGHJ1bnRpbWUuc3BhbkNsYXNzLm5vc2NhbgoYcnVudGltZS4oKmdjV29yaykudHJ5R2V0Clxnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci4oKm1ldHJpY3NTZW5kZXJXaXRoT2JzZXJ2YWJpbGl0eSkuc2VuZApbZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvZXhwb3J0ZXIvZXhwb3J0ZXJoZWxwZXIuKCpsb2dzRXhwb3J0ZXJXaXRoT2JzZXJ2YWJpbGl0eSkuc2VuZAobbmV0LigqY29uZikuaG9zdExvb2t1cE9yZGVyCgpydW50aW1lLkdDCkNnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL2dvZGVsdGFwcm9mLigqSGVhcFByb2ZpbGVyKS5Qcm9maWxlClBnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9yZWNlaXZlci9yZWNlaXZlcmhlbHBlci4oKk9ic1JlcG9ydCkucmVjb3JkTWV0cmljcwpIZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvY29uc3VtZXIuQ29uc3VtZU1ldHJpY3NGdW5jLkNvbnN1bWVNZXRyaWNzCkNnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9wZGF0YS9wbWV0cmljLk1ldHJpY1NsaWNlLkFwcGVuZEVtcHR5CkBnby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9wZGF0YS9wbWV0cmljLk1ldHJpYy5TZXRFbXB0eUdhdWdlCntnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcmVjZWl2ZXIvcHJvbWV0aGV1c3JlY2VpdmVyL2ludGVybmFsLigqbWV0cmljRmFtaWx5KS5hcHBlbmRNZXRyaWMKigFnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcmVjZWl2ZXIvcHJvbWV0aGV1c3JlY2VpdmVyL2ludGVybmFsLigqaW5pdGlhbFBvaW50QWRqdXN0ZXIpLmFkanVzdE1ldHJpY1N1bW1hcnkKGGlvLigqTGltaXRlZFJlYWRlcikuUmVhZAoRcnVudGltZS5tYXBhc3NpZ24KDW5ldC9odHRwLnNlbmQKPWdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL3NjcmFwZS5uZXdTY3JhcGVQb29sLmZ1bmMxLjIKdGdpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9yZWNlaXZlci9wcm9tZXRoZXVzcmVjZWl2ZXIvaW50ZXJuYWwuKCp0cmFuc2FjdGlvbikuQXBwZW5kChh1bmljb2RlL3V0ZjguVmFsaWRTdHJpbmcKEXJ1bnRpbWUubWFrZXNsaWNlCmtnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcmVjZWl2ZXIvcHJvbWV0aGV1c3JlY2VpdmVyL2ludGVybmFsLmdldFNlcmllc1JlZgogaW50ZXJuYWwvYnl0ZWFsZy5JbmRleEJ5dGVTdHJpbmcKEXJ1bnRpbWUucHJvY3lpZWxkChZuZXQvaHR0cC5wYXJzZVBvc3RGb3JtChRydW50aW1lLnByb2ZpbGVhbGxvYwo1Z2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS4oKlByb2ZpbGUpLkNoZWNrVmFsaWQKKWdpdGh1Yi5jb20vZ29vZ2xlL3Bwcm9mL3Byb2ZpbGUudW5tYXJzaGFsChRieXRlcy4oKkJ1ZmZlcikuUmVhZAoxZ2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS4oKlByb2ZpbGUpLmVuY29kZQoXcnVudGltZS4oKmxmc3RhY2spLnB1c2gKDHJ1bnRpbWUubXB1dAoRcnVudGltZS5ub3Rlc2xlZXAKEXJ1bnRpbWUucnVucXN0ZWFsCiJydW50aW1lL2ludGVybmFsL3N5c2NhbGwuRXBvbGxXYWl0ChJydW50aW1lLm5vdGV3YWtldXAKEnJ1bnRpbWUuY2FzZ3N0YXR1cwoccnVudGltZS4oKnJhbmRvbU9yZGVyKS5zdGFydAoQcnVudGltZS5maW5kbnVsbAopZ29sYW5nLm9yZy94L25ldC90cmFjZS4oKmV2ZW50TG9nKS5FcnJvcmYKGnJ1bnRpbWUuKCptY2FjaGUpLm5leHRGcmVlChpydW50aW1lLlJlYWRNZW1TdGF0cy5mdW5jMQoNc3lzY2FsbC5Xcml0ZQoPcnVudGltZS5nZXRpdGFiCh1uZXQuKCpzeXNEaWFsZXIpLmRpYWxQYXJhbGxlbAoZaW50ZXJuYWwvcG9sbC4oKkZEKS5DbG9zZQoUcnVudGltZS4oKmJ1Y2tldCkubXAKKXJ1bnRpbWUuKCpnY0NvbnRyb2xsZXJTdGF0ZSkuZW5saXN0V29ya2VyCg9ydW50aW1lLmhhbmRvZmYKFXJ1bnRpbWUubWFya3Jvb3RCbG9jawoWcnVudGltZS5tYXJrcm9vdC5mdW5jMQoacnVudGltZS5tYXJrQml0cy5zZXRNYXJrZWQKEnJ1bnRpbWUuZ3JleW9iamVjdAoScnVudGltZS5maW5kT2JqZWN0ChNydW50aW1lLnJlYWRVaW50cHRyChNydW50aW1lLnBhZ2VJbmRleE9mChVydW50aW1lLigqbXNwYW4pLmJhc2UKGXJ1bnRpbWUubWFya0JpdHMuaXNNYXJrZWQKKHJ1bnRpbWUuKCptc3BhbikudHlwZVBvaW50ZXJzT2ZVbmNoZWNrZWQKDnJ1bnRpbWUuc3Bhbk9mChZydW50aW1lLigqZ2NCaXRzKS5iaXRwChlydW50aW1lLnR5cGVQb2ludGVycy5uZXh0ChhydW50aW1lLndiQnVmRmx1c2guZnVuYzEKFnJ1bnRpbWUuKCpsZnN0YWNrKS5wb3AKEHJ1bnRpbWUucHV0ZW1wdHkKSWdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2V4cG9ydGVyL2V4cG9ydGVyaGVscGVyLigqcmV0cnlTZW5kZXIpLnNlbmQKT2dvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2V4cG9ydGVyL2V4cG9ydGVyaGVscGVyLigqT2JzUmVwb3J0KS5FbmRNZXRyaWNzT3AKSmdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2V4cG9ydGVyL2V4cG9ydGVyaGVscGVyLigqT2JzUmVwb3J0KS5zdGFydE9wChduZXQuKCpjb25mKS5sb29rdXBPcmRlcgpeZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby9nb2RlbHRhcHJvZi9pbnRlcm5hbC9wcHJvZi4oKkRlbHRhSGVhcFByb2ZpbGVyKS5Xcml0ZUhlYXBQcm90bwo6Z28ub3BlbnRlbGVtZXRyeS5pby9vdGVsL3Nkay9tZXRyaWMuKCppbnQ2NEluc3QpLmFnZ3JlZ2F0ZQo3Z2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvbW9kZWwvdmFsdWUuSXNTdGFsZU5hTgpMZ28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvcGRhdGEvcG1ldHJpYy5OdW1iZXJEYXRhUG9pbnRTbGljZS5BcHBlbmRFbXB0eQp4Z2l0aHViLmNvbS9vcGVuLXRlbGVtZXRyeS9vcGVudGVsZW1ldHJ5LWNvbGxlY3Rvci1jb250cmliL3JlY2VpdmVyL3Byb21ldGhldXNyZWNlaXZlci9pbnRlcm5hbC4oKm1ldHJpY0dyb3VwKS5zb3J0UG9pbnRzCmdnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcGtnL3RyYW5zbGF0b3IvcHJvbWV0aGV1cy53b3JkVG9VQ1VNT3JEZWZhdWx0CnNnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcmVjZWl2ZXIvcHJvbWV0aGV1c3JlY2VpdmVyL2ludGVybmFsLigqdGltZXNlcmllc01hcCkuZ2V0CjFnaXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9nemlwLigqUmVhZGVyKS5SZWFkChxydW50aW1lLm1lbWNsck5vSGVhcFBvaW50ZXJzCkZnaXRodWIuY29tL3Byb21ldGhldXMvY29tbW9uL2NvbmZpZy4oKnVzZXJBZ2VudFJvdW5kVHJpcHBlcikuUm91bmRUcmlwCkBnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9zY3JhcGUubXV0YXRlUmVwb3J0U2FtcGxlTGFiZWxzCnJnaXRodWIuY29tL29wZW4tdGVsZW1ldHJ5L29wZW50ZWxlbWV0cnktY29sbGVjdG9yLWNvbnRyaWIvcmVjZWl2ZXIvcHJvbWV0aGV1c3JlY2VpdmVyL2ludGVybmFsLm5vcm1hbGl6ZU1ldHJpY05hbWUKHmdvLnViZXIub3JnL3phcC4oKkxvZ2dlcikuV2FybgqFAWdpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9yZWNlaXZlci9wcm9tZXRoZXVzcmVjZWl2ZXIvaW50ZXJuYWwuKCp0cmFuc2FjdGlvbikuZ2V0T3JDcmVhdGVNZXRyaWNGYW1pbHkKRmdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL21vZGVsL2xhYmVscy5MYWJlbHMuSGFzaFdpdGhvdXRMYWJlbHMKE21pbWUuUGFyc2VNZWRpYVR5cGUKFHJ1bnRpbWUubVByb2ZfTWFsbG9jChlydW50aW1lLm1hcGFjY2VzczFfZmFzdDY0Ci1naXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLmRlY29kZU1lc3NhZ2UKD3J1bnRpbWUubWVtbW92ZQotZ2l0aHViLmNvbS9nb29nbGUvcHByb2YvcHJvZmlsZS5lbmNvZGVNZXNzYWdlChxjb21wcmVzcy9nemlwLigqUmVhZGVyKS5SZWFkCiZydW50aW1lL2ludGVybmFsL2F0b21pYy4oKlVpbnQzMikuTG9hZAoScnVudGltZS5mdXRleHNsZWVwChBydW50aW1lLnJ1bnFncmFiCiFydW50aW1lL2ludGVybmFsL3N5c2NhbGwuU3lzY2FsbDYKE3J1bnRpbWUuZnV0ZXh3YWtldXAKDWluZGV4Ynl0ZWJvZHkKKWdvbGFuZy5vcmcveC9uZXQvdHJhY2UuKCpldmVudExvZykucHJpbnRmChhydW50aW1lLigqbWNhY2hlKS5yZWZpbGwKFnJ1bnRpbWUucmVhZG1lbXN0YXRzX20KG3J1bnRpbWUuc3RhcnRUaGVXb3JsZC5mdW5jMQoacnVudGltZS5zdG9wVGhlV29ybGQuZnVuYzEKDXN5c2NhbGwud3JpdGUKG25ldC4oKnN5c0RpYWxlcikuZGlhbFNlcmlhbAoaaW50ZXJuYWwvcG9sbC4oKkZEKS5kZWNyZWYKEHJ1bnRpbWUucHJlZW1wdE0KEHJ1bnRpbWUuZ2V0ZW1wdHkKD3J1bnRpbWUucHV0ZnVsbAoRcnVudGltZS5zY2FuYmxvY2sKEXJ1bnRpbWUuc2NhbnN0YWNrCiVydW50aW1lLigqbXNwYW4pLmhlYXBCaXRzU21hbGxGb3JBZGRyCgxydW50aW1lLmFkZGIKS2dvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2V4cG9ydGVyL2V4cG9ydGVyaGVscGVyLigqdGltZW91dFNlbmRlcikuc2VuZAo9Z28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvZXhwb3J0ZXIvZXhwb3J0ZXJoZWxwZXIuZW5kU3BhbgoyZ28ub3BlbnRlbGVtZXRyeS5pby9vdGVsL3Nkay90cmFjZS4oKnRyYWNlcikuU3RhcnQKEHJ1bnRpbWUubmV3c3RhY2sKWWdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vZ29kZWx0YXByb2YvaW50ZXJuYWwvcHByb2YuKCpwcm9maWxlQnVpbGRlcikuTG9jc0ZvclN0YWNrChFydW50aW1lLkZ1bmNGb3JQQwpSZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby9nb2RlbHRhcHJvZi9pbnRlcm5hbC9wcHJvZi4oKnByb2ZpbGVCdWlsZGVyKS5CdWlsZArgAmdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vZ29kZWx0YXByb2YvaW50ZXJuYWwvcHByb2YuKCpwcm9mTWFwW2dvLnNoYXBlLnN0cnVjdCB7IGdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vZ29kZWx0YXByb2YvaW50ZXJuYWwvcHByb2YuYWxsb2NPYmplY3RzIGludDY0IH0sZ28uc2hhcGUuc3RydWN0IHsgZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby9nb2RlbHRhcHJvZi9pbnRlcm5hbC9wcHJvZi5hbGxvY09iamVjdHMgaW50NjQ7IGdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vZ29kZWx0YXByb2YvaW50ZXJuYWwvcHByb2YuaW51c2VPYmplY3RzIGludDY0IH1dKS5Mb29rdXAKUWdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3Byb2Nlc3Nvci9wcm9jZXNzb3JoZWxwZXIuTmV3TWV0cmljc1Byb2Nlc3Nvci5mdW5jMQoScnVudGltZS5tYXBhY2Nlc3MyCjhnaXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9mbGF0ZS4oKmRlY29tcHJlc3NvcikuUmVhZAofbmV0L2h0dHAuKCpUcmFuc3BvcnQpLlJvdW5kVHJpcAo6Z2l0aHViLmNvbS9wcm9tZXRoZXVzL3Byb21ldGhldXMvbW9kZWwvbGFiZWxzLkxhYmVscy5SYW5nZQofZ28udWJlci5vcmcvemFwLigqTG9nZ2VyKS5jaGVjawpuZ2l0aHViLmNvbS9vcGVuLXRlbGVtZXRyeS9vcGVudGVsZW1ldHJ5LWNvbGxlY3Rvci1jb250cmliL3JlY2VpdmVyL3Byb21ldGhldXNyZWNlaXZlci9pbnRlcm5hbC5uZXdNZXRyaWNGYW1pbHkKGnJ1bnRpbWUuZGVkdWN0QXNzaXN0Q3JlZGl0ChFydW50aW1lLmNtcHN0cmluZwoLc3RyaW5ncy5DdXQKEXJ1bnRpbWUuc3RrYnVja2V0CitnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLmluaXQuZnVuYzM2CitnaXRodWIuY29tL2dvb2dsZS9wcHJvZi9wcm9maWxlLmluaXQuZnVuYzM3CiNjb21wcmVzcy9mbGF0ZS4oKmRlY29tcHJlc3NvcikuUmVhZAoNcnVudGltZS5mdXRleAoLZm10LlNwcmludGYKHXJ1bnRpbWUuKCptY2VudHJhbCkuY2FjaGVTcGFuChdydW50aW1lLmZsdXNoYWxsbWNhY2hlcwodcnVudGltZS5zdGFydFRoZVdvcmxkV2l0aFNlbWEKHHJ1bnRpbWUuc3RvcFRoZVdvcmxkV2l0aFNlbWEKD3N5c2NhbGwuU3lzY2FsbAobbmV0Ligqc3lzRGlhbGVyKS5kaWFsU2luZ2xlCh9pbnRlcm5hbC9wb2xsLigqcG9sbERlc2MpLmNsb3NlCg9ydW50aW1lLnNpZ25hbE0KF3J1bnRpbWUuc2NhbmZyYW1ld29ya2VyCk5nby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9leHBvcnRlci9leHBvcnRlcmhlbHBlci4oKm1ldHJpY3NSZXF1ZXN0KS5FeHBvcnQKN2dvLm9wZW50ZWxlbWV0cnkuaW8vb3RlbC9zZGsvdHJhY2UuKCpyZWNvcmRpbmdTcGFuKS5FbmQKS2dvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL2V4cG9ydGVyL2V4cG9ydGVyaGVscGVyLigqbG9nc1JlcXVlc3QpLkV4cG9ydAo0Z28ub3BlbnRlbGVtZXRyeS5pby9vdGVsL3Nkay90cmFjZS4oKnRyYWNlcikubmV3U3BhbgoRcnVudGltZS5jb3B5c3RhY2sKF3J1bnRpbWUuKCpzcGFuU2V0KS5wdXNoCllnaXRodWIuY29tL2dyYWZhbmEvcHlyb3Njb3BlLWdvL2dvZGVsdGFwcm9mL2ludGVybmFsL3Bwcm9mLigqcHJvZmlsZUJ1aWxkZXIpLmVtaXRMb2NhdGlvbgpEZ2l0aHViLmNvbS9ncmFmYW5hL3B5cm9zY29wZS1nby9nb2RlbHRhcHJvZi9pbnRlcm5hbC9wcHJvZi5hbGxGcmFtZXMKWGdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vZ29kZWx0YXByb2YvaW50ZXJuYWwvcHByb2YuKCpwcm9maWxlQnVpbGRlcikuc3RyaW5nSW5kZXgKKXJ1bnRpbWUuKCppbmxpbmVVbndpbmRlcikucmVzb2x2ZUludGVybmFsCjNnaXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9mbGF0ZS4oKldyaXRlcikuQ2xvc2UKVGdvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3Byb2Nlc3Nvci9wcm9jZXNzb3JoZWxwZXIuKCpPYnNSZXBvcnQpLk1ldHJpY3NBY2NlcHRlZAoTcnVudGltZS5oZWFwU2V0VHlwZQp1dHlwZTouZXEuZ2l0aHViLmNvbS9vcGVuLXRlbGVtZXRyeS9vcGVudGVsZW1ldHJ5LWNvbGxlY3Rvci1jb250cmliL3JlY2VpdmVyL3Byb21ldGhldXNyZWNlaXZlci9pbnRlcm5hbC50aW1lc2VyaWVzS2V5CjpnaXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9mbGF0ZS4oKmRlY29tcHJlc3NvcikuZG9TdGVwCh9uZXQvaHR0cC4oKlRyYW5zcG9ydCkucm91bmRUcmlwCkNnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9zY3JhcGUuKCpUYXJnZXQpLkxhYmVsc1JhbmdlLmZ1bmMxCg9ydW50aW1lLkNhbGxlcnMKE3J1bnRpbWUucm91bmR1cHNpemUKKGNvbXByZXNzL2ZsYXRlLigqZGVjb21wcmVzc29yKS5uZXh0QmxvY2sKF3NvcnQuaW5zZXJ0aW9uU29ydF9mdW5jChJmbXQuKCpwcCkuZG9QcmludGYKGHJ1bnRpbWUuKCptY2VudHJhbCkuZ3JvdwoTcnVudGltZS5mbHVzaG1jYWNoZQoScnVudGltZS5wcm9jcmVzaXplChJydW50aW1lLm5vdGV0c2xlZXAKE3N5c2NhbGwuUmF3U3lzY2FsbDYKGG5ldC4oKnN5c0RpYWxlcikuZGlhbFVEUAofaW50ZXJuYWwvcG9sbC5ydW50aW1lX3BvbGxDbG9zZQoOcnVudGltZS50Z2tpbGwKFXJ1bnRpbWUuKCpnY1dvcmspLnB1dApaZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci4oKm1ldHJpY3NFeHBvcnRlcikucHVzaE1ldHJpY3NEYXRhCjlnby5vcGVudGVsZW1ldHJ5LmlvL2NvbnRyaWIvenBhZ2VzLigqU3BhblByb2Nlc3NvcikuT25FbmQKZmdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9jbGlja2hvdXNlcHJvZmlsZWV4cG9ydGVyLigqY2xpY2tob3VzZVByb2ZpbGVFeHBvcnRlcikuc2VuZAo1Z28ub3BlbnRlbGVtZXRyeS5pby9vdGVsL3RyYWNlLlNwYW5Db250ZXh0RnJvbUNvbnRleHQKE3J1bnRpbWUuYWRqdXN0ZnJhbWUKGXJ1bnRpbWUubWFwYXNzaWduX2Zhc3RzdHIKUmdpdGh1Yi5jb20vZ3JhZmFuYS9weXJvc2NvcGUtZ28vZ29kZWx0YXByb2YvaW50ZXJuYWwvcHByb2YuKCpwcm9maWxlQnVpbGRlcikuZmx1c2gKFnJ1bnRpbWUuKCpGcmFtZXMpLk5leHQKGHJ1bnRpbWUuZnVuY05hbWVGb3JQcmludAoUcnVudGltZS5zcmNGdW5jLm5hbWUKFHJ1bnRpbWUucGNkYXRhdmFsdWUxCjdnaXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9mbGF0ZS4oKmNvbXByZXNzb3IpLmNsb3NlCk9nby5vcGVudGVsZW1ldHJ5LmlvL2NvbGxlY3Rvci9wcm9jZXNzb3IvcHJvY2Vzc29yaGVscGVyLigqT2JzUmVwb3J0KS5yZWNvcmREYXRhCiNydW50aW1lLigqbXNwYW4pLndyaXRlSGVhcEJpdHNTbWFsbAoJbWVtZXFib2R5Cj1naXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9mbGF0ZS4oKmRlY29tcHJlc3NvcikubmV4dEJsb2NrChljb250ZXh0LigqY2FuY2VsQ3R4KS5Eb25lCh1uZXQvaHR0cC4oKlRyYW5zcG9ydCkuZ2V0Q29ubgohbmV0L2h0dHAuKCpwZXJzaXN0Q29ubikucm91bmRUcmlwCkZnaXRodWIuY29tL3Byb21ldGhldXMvcHJvbWV0aGV1cy9zY3JhcGUubXV0YXRlUmVwb3J0U2FtcGxlTGFiZWxzLmZ1bmMxCg9ydW50aW1lLmNhbGxlcnMKK2NvbXByZXNzL2ZsYXRlLigqZGVjb21wcmVzc29yKS5odWZmbWFuQmxvY2sKUmdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9yZWNlaXZlci9weXJvc2NvcGVyZWNlaXZlci5wb3N0UHJvY2Vzc1Byb2YuZnVuYzIKEmZtdC4oKnBwKS5wcmludEFyZwoWcnVudGltZS4oKm1oZWFwKS5hbGxvYwoYcnVudGltZS5zdGFja2NhY2hlX2NsZWFyChxydW50aW1lLigqbWNhY2hlKS5yZWxlYXNlQWxsChtydW50aW1lLm5vdGV0c2xlZXBfaW50ZXJuYWwKEm5ldC5pbnRlcm5ldFNvY2tldAohcnVudGltZS9pbnRlcm5hbC9zeXNjYWxsLkVwb2xsQ3RsCiBydW50aW1lLigqc3RhY2tTY2FuU3RhdGUpLnB1dFB0cgpdZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci4oKm1ldHJpY3NFeHBvcnRlcikuY29sbGVjdEZyb21NZXRyaWNzClFnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLmJhdGNoU2FtcGxlc0FuZFRpbWVTZXJpZXMKdWdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9jbGlja2hvdXNlcHJvZmlsZWV4cG9ydGVyL2NoLigqY2xpY2tob3VzZUFjY2Vzc05hdGl2ZUNvbHVtbmFyKS5JbnNlcnRCYXRjaAouZ28ub3BlbnRlbGVtZXRyeS5pby9vdGVsL3RyYWNlLlNwYW5Gcm9tQ29udGV4dAofcnVudGltZS4oKnN0a2ZyYW1lKS5nZXRTdGFja01hcAoUcnVudGltZS5uZXh0RnJlZUZhc3QKGHJ1bnRpbWUuZ3Jvd1dvcmtfZmFzdHN0cgotZ2l0aHViLmNvbS9rbGF1c3Bvc3QvY29tcHJlc3MvZmxhdGUuTmV3V3JpdGVyChpydW50aW1lLigqX2Z1bmMpLmlzSW5saW5lZAoRcnVudGltZS5mdW5jbGluZTEKHnJ1bnRpbWUuZnVuY05hbWVQaWVjZXNGb3JQcmludAoPcnVudGltZS5wY3ZhbHVlCjtnaXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9mbGF0ZS4oKmNvbXByZXNzb3IpLnN0b3JlRmFzdAo0Z28ub3BlbnRlbGVtZXRyeS5pby9vdGVsL3Nkay9tZXRyaWMuKCppbnQ2NEluc3QpLkFkZAqhAWdpdGh1Yi5jb20vb3Blbi10ZWxlbWV0cnkvb3BlbnRlbGVtZXRyeS1jb2xsZWN0b3ItY29udHJpYi9yZWNlaXZlci9wcm9tZXRoZXVzcmVjZWl2ZXIvaW50ZXJuYWwuKCptZXRyaWNHcm91cCkudG9EaXN0cmlidXRpb25Qb2ludC4oKm1ldHJpY0dyb3VwKS5zb3J0UG9pbnRzLmZ1bmMxCkdnaXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9mbGF0ZS4oKmRlY29tcHJlc3NvcikuaHVmZm1hbkJsb2NrRGVjb2RlcgoQcnVudGltZS5tYWtlY2hhbgombmV0L2h0dHAuKCpUcmFuc3BvcnQpLnF1ZXVlRm9ySWRsZUNvbm4KEXJ1bnRpbWUuY2hhbnNlbmQxChhydW50aW1lLigqdW53aW5kZXIpLmluaXQKPGdpdGh1Yi5jb20vcHJvbWV0aGV1cy9wcm9tZXRoZXVzL21vZGVsL2xhYmVscy4oKkJ1aWxkZXIpLkRlbAoVcnVudGltZS5jb25jYXRzdHJpbmcyChhydW50aW1lLigqdW53aW5kZXIpLm5leHQKJmNvbXByZXNzL2ZsYXRlLigqZGVjb21wcmVzc29yKS5odWZmU3ltChdmbXQuKCpwcCkuaGFuZGxlTWV0aG9kcwoXcnVudGltZS5zcGFuT2ZVbmNoZWNrZWQKH3J1bnRpbWUuKCptY2VudHJhbCkudW5jYWNoZVNwYW4KCm5ldC5zb2NrZXQKJG5ldC9odHRwLigqVHJhbnNwb3J0KS5zZXRSZXFDYW5jZWxlcgpcZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci4oKm1ldHJpY3NFeHBvcnRlcikuY29sbGVjdEZyb21NZXRyaWMKNGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqYmF0Y2gpLlNlbmQKPGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqYmF0Y2gpLkFwcGVuZFN0cnVjdApCZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci5pbml0LmZ1bmMyCkFnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmNsaWNraG91c2UpLlByZXBhcmVCYXRjaAo8Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpiYXRjaENvbHVtbikuQXBwZW5kClpnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvY2xpY2tob3VzZXByb2ZpbGVleHBvcnRlci9jaC4oKkxpbWl0ZWRQb29sKS5wdXQKV2dpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9jbGlja2hvdXNlcHJvZmlsZWV4cG9ydGVyL2NoLnJlYWRUcmVlRnJvbU1hcAoaY29udGV4dC4oKmNhbmNlbEN0eCkuVmFsdWUKE3J1bnRpbWUucGNkYXRhdmFsdWUKGHJ1bnRpbWUuKCpibWFwKS5vdmVyZmxvdwoYcnVudGltZS5ldmFjdWF0ZV9mYXN0c3RyCgxydW50aW1lLnN0ZXAKLGdpdGh1Yi5jb20va2xhdXNwb3N0L2NvbXByZXNzL2ZsYXRlLmxvYWQzMjMyCklnaXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9mbGF0ZS4oKmh1ZmZtYW5CaXRXcml0ZXIpLndyaXRlQmxvY2tEeW5hbWljCiNlbmNvZGluZy9iaW5hcnkubGl0dGxlRW5kaWFuLlVpbnQ2NAosZ28ub3BlbnRlbGVtZXRyeS5pby9vdGVsL21ldHJpYy5OZXdBZGRDb25maWcKRmdpdGh1Yi5jb20va2xhdXNwb3N0L2NvbXByZXNzL2ZsYXRlLigqZGVjb21wcmVzc29yKS5odWZmbWFuQnVmaW9SZWFkZXIKH25ldC9odHRwLigqd2FudENvbm4pLnRyeURlbGl2ZXIKEHJ1bnRpbWUuY2hhbnNlbmQKGnJ1bnRpbWUuKCp1bndpbmRlcikuaW5pdEF0ChVydW50aW1lLmNvbmNhdHN0cmluZ3MKFXJ1bnRpbWUuY2FsbGVycy5mdW5jMQojcnVudGltZS4oKnVud2luZGVyKS5yZXNvbHZlSW50ZXJuYWwKGGJ1ZmlvLigqUmVhZGVyKS5SZWFkQnl0ZQoUbmV0LigqT3BFcnJvcikuRXJyb3IKDW5ldC5zeXNTb2NrZXQKEW5ldC4oKm5ldEZEKS5kaWFsChtydW50aW1lLm1hcGFzc2lnbl9mYXN0NjRwdHIKYWdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIuKCptZXRyaWNzRXhwb3J0ZXIpLmV4cG9ydE51bWJlckRhdGFQb2ludHMKZGdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIuKCptZXRyaWNzRXhwb3J0ZXIpLmV4cG9ydEhpc3RvZ3JhbURhdGFQb2ludHMKOmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY29ubmVjdCkuc2VuZERhdGEKN2dpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqYmF0Y2gpLnJlbGVhc2UKOmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqYmF0Y2gpLmNsb3NlUXVlcnkKN2dpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqc3RydWN0TWFwKS5NYXAKNmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqYmF0Y2gpLkFwcGVuZAo+Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjb25uZWN0KS5wcmVwYXJlQmF0Y2gKQWdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpBcnJheSkuQXBwZW5kChJydW50aW1lLmNvbnZUc2xpY2UKWmdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9jbGlja2hvdXNlcHJvZmlsZWV4cG9ydGVyL2NoLigqTGltaXRlZFBvb2wpLmdldAoaZW5jb2RpbmcvYmluYXJ5LlJlYWRWYXJpbnQKDWNvbnRleHQudmFsdWUKEnJ1bnRpbWUucmVhZHZhcmludApAZ2l0aHViLmNvbS9rbGF1c3Bvc3QvY29tcHJlc3MvZmxhdGUuKCpodWZmbWFuQml0V3JpdGVyKS5nZW5lcmF0ZQoPcnVudGltZS5nb3JlYWR5ChNydW50aW1lLmZ1bmNzcGRlbHRhChFydW50aW1lLnJhd3N0cmluZwoUcnVudGltZS50cmFjZWJhY2tQQ3MKFW5ldC4oKkROU0Vycm9yKS5FcnJvcgoOc3lzY2FsbC5Tb2NrZXQKFG5ldC4oKm5ldEZEKS5jb25uZWN0CmBnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLigqbWV0cmljc0V4cG9ydGVyKS5leHBvcnROdW1iZXJEYXRhUG9pbnQKN2dpdGh1Yi5jb20vcHJvbWV0aGV1cy9jb21tb24vbW9kZWwuTGFiZWxTZXQuRmluZ2VycHJpbnQKY2dpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIuKCptZXRyaWNzRXhwb3J0ZXIpLmV4cG9ydEhpc3RvZ3JhbURhdGFQb2ludApGZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL3Byb3RvLigqQmxvY2spLkVuY29kZUNvbHVtbgo8Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjbGlja2hvdXNlKS5yZWxlYXNlCjlnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmNvbm5lY3QpLnByb2Nlc3MKGnJ1bnRpbWUubWFwYWNjZXNzMl9mYXN0c3RyCkBnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvcHJvdG8uKCpCbG9jaykuQXBwZW5kChZyZWdleHAuKCpSZWdleHApLlNwbGl0CjtnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi4oKmNvbm5lY3QpLnNlbmRRdWVyeQo8Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjb25uZWN0KS5maXJzdEJsb2NrCkRnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqQXJyYXkpLkFwcGVuZFJvdwo3Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjb25uZWN0KS5mbHVzaAoQc3luYy4oKlBvb2wpLkdldAoPcnVudGltZS5lZmFjZWVxCj5naXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9mbGF0ZS4oKmh1ZmZtYW5FbmNvZGVyKS5nZW5lcmF0ZQoWcnVudGltZS5mdW5jSW5mby5lbnRyeQoOc3lzY2FsbC5zb2NrZXQKGGludGVybmFsL3BvbGwuKCpGRCkuSW5pdApFZ2l0aHViLmNvbS9tZXRyaWNvL290ZWwtY29sbGVjdG9yL2V4cG9ydGVyL3FyeW5leHBvcnRlci5idWlsZExhYmVsU2V0Ck5naXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLmJ1aWxkUHJvbUNvbXBsaWFudE5hbWUKFWVuY29kaW5nL2pzb24uTWFyc2hhbAo4Z2l0aHViLmNvbS9wcm9tZXRoZXVzL2NvbW1vbi9tb2RlbC5sYWJlbFNldFRvRmluZ2VycHJpbnQKKmdpdGh1Yi5jb20vcHJvbWV0aGV1cy9jb21tb24vbW9kZWwuaGFzaEFkZApCZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL2NvbHVtbi4oKlN0cmluZykuRW5jb2RlChRydW50aW1lLnNlbGVjdG5icmVjdgo0Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NoLWdvL3Byb3RvLigqUmVhZGVyKS5SZWFkQnl0ZQo4Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjb25uZWN0KS5oYW5kbGUKNGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jaC1nby9wcm90by4oKlJlYWRlcikucmVhZEZ1bGwKMmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jaC1nby9wcm90by4oKkNvbFN0cikuQXBwZW5kCiNyZWdleHAuKCpSZWdleHApLkZpbmRBbGxTdHJpbmdJbmRleApJZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL2NvbHVtbi4oKkFycmF5KS5hcHBlbmRSb3dQbGFpbgoUc3luYy4oKlBvb2wpLmdldFNsb3cKEXJ1bnRpbWUucnVuR0NQcm9nCj9naXRodWIuY29tL2tsYXVzcG9zdC9jb21wcmVzcy9mbGF0ZS4oKmh1ZmZtYW5FbmNvZGVyKS5iaXRDb3VudHMKGnJ1bnRpbWUuc2VuZC5nb3JlYWR5LmZ1bmMxChJzeXNjYWxsLlJhd1N5c2NhbGwKHmludGVybmFsL3BvbGwuKCpwb2xsRGVzYykuaW5pdAo1Z28ub3BlbnRlbGVtZXRyeS5pby9jb2xsZWN0b3IvcGRhdGEvcGNvbW1vbi5NYXAuUmFuZ2UKUGdpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIucmVtb3ZlUHJvbUZvcmJpZGRlblJ1bmVzCiRlbmNvZGluZy9qc29uLigqZW5jb2RlU3RhdGUpLm1hcnNoYWwKM2dvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3BkYXRhL3Bjb21tb24uTWFwLkdldAoQcnVudGltZS5yZWxlYXNlbQoJc29ydC5Tb3J0CjVnaXRodWIuY29tL0NsaWNrSG91c2UvY2gtZ28vcHJvdG8uQ29sU3RyLkVuY29kZUNvbHVtbgoQcnVudGltZS5jaGFucmVjdgo/Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIuKCpjb25uZWN0KS5wcm9maWxlRXZlbnRzCgtpby5SZWFkRnVsbAobcmVnZXhwLigqUmVnZXhwKS5hbGxNYXRjaGVzCktnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqQXJyYXkpLmFwcGVuZFJvd0RlZmF1bHQKGXN5bmMuKCpwb29sQ2hhaW4pLnBvcFRhaWwKDXJ1bnRpbWUucmVhZHkKEXJ1bnRpbWUuY2hlYXByYW5kCh5pbnRlcm5hbC9wb2xsLnJ1bnRpbWVfcG9sbE9wZW4KS2dpdGh1Yi5jb20vbWV0cmljby9vdGVsLWNvbGxlY3Rvci9leHBvcnRlci9xcnluZXhwb3J0ZXIuYnVpbGRMYWJlbFNldC5mdW5jMQoSc3RyaW5ncy5GaWVsZHNGdW5jCillbmNvZGluZy9qc29uLigqZW5jb2RlU3RhdGUpLnJlZmxlY3RWYWx1ZQoMc29ydC5wZHFzb3J0CkJnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqRW51bTgpLlNjYW5Sb3cKOmdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyLigqY29ubmVjdCkucmVhZERhdGEKDmlvLlJlYWRBdExlYXN0ChpyZWdleHAuKCpSZWdleHApLmRvRXhlY3V0ZQpBZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL2NvbHVtbi4oKkFycmF5KS5hcHBlbmQKX2dvLm9wZW50ZWxlbWV0cnkuaW8vY29sbGVjdG9yL3BkYXRhL2ludGVybmFsL2RhdGEvcHJvdG9nZW4vY29tbW9uL3YxLigqQW55VmFsdWUpLkdldFN0cmluZ1ZhbHVlClZnaXRodWIuY29tL21ldHJpY28vb3RlbC1jb2xsZWN0b3IvZXhwb3J0ZXIvcXJ5bmV4cG9ydGVyLnJlbW92ZVByb21Gb3JiaWRkZW5SdW5lcy5mdW5jMQofZW5jb2RpbmcvanNvbi5tYXBFbmNvZGVyLmVuY29kZQqGAXNsaWNlcy5Tb3J0RnVuY1tnby5zaGFwZS5bXWVuY29kaW5nL2pzb24ucmVmbGVjdFdpdGhTdHJpbmcsZ28uc2hhcGUuc3RydWN0IHsgZW5jb2RpbmcvanNvbi52IHJlZmxlY3QuVmFsdWU7IGVuY29kaW5nL2pzb24ua3Mgc3RyaW5nIH1dChJzb3J0Lmluc2VydGlvblNvcnQKEnJ1bnRpbWUubWFwYWNjZXNzMQpAZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL3Byb3RvLigqQmxvY2spLkRlY29kZQowZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NoLWdvL3Byb3RvLigqUmVhZGVyKS5SZWFkChZydW50aW1lLmdjVHJpZ2dlci50ZXN0ChpyZWdleHAuKCpSZWdleHApLmJhY2t0cmFjawoMcnVudGltZS5tZ2V0ChlieXRlcy4oKkJ1ZmZlcikuV3JpdGVCeXRlChdyZWZsZWN0LigqTWFwSXRlcikuTmV4dAoWcmVmbGVjdC4oKk1hcEl0ZXIpLktleQoYcmVmbGVjdC4oKk1hcEl0ZXIpLlZhbHVlCmFzbGljZXMucGRxc29ydENtcEZ1bmNbZ28uc2hhcGUuc3RydWN0IHsgZW5jb2RpbmcvanNvbi52IHJlZmxlY3QuVmFsdWU7IGVuY29kaW5nL2pzb24ua3Mgc3RyaW5nIH1dChtlbmNvZGluZy9qc29uLnN0cmluZ0VuY29kZXIKMmdpdGh1Yi5jb20vcHJvbWV0aGV1cy9jb21tb24vbW9kZWwuTGFiZWxOYW1lcy5MZXNzChBydW50aW1lLm1lbWhhc2g4Cj1naXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLlR5cGUuQ29sdW1uCkRnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqRGF0ZVRpbWUpLkRlY29kZQoUYnVmaW8uKCpSZWFkZXIpLlJlYWQKGHJlZ2V4cC4oKmJpdFN0YXRlKS5yZXNldApEZ2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL2NvbHVtbi4oKlR1cGxlKS5BcHBlbmRSb3cKE3JlZmxlY3QubWFwaXRlcm5leHQKD3JlZmxlY3QuY29weVZhbApnc2xpY2VzLmluc2VydGlvblNvcnRDbXBGdW5jW2dvLnNoYXBlLnN0cnVjdCB7IGVuY29kaW5nL2pzb24udiByZWZsZWN0LlZhbHVlOyBlbmNvZGluZy9qc29uLmtzIHN0cmluZyB9XQoVYnl0ZXMuKCpCdWZmZXIpLldyaXRlCgdjbXBib2R5CgthZXNoYXNoYm9keQo2Z2l0aHViLmNvbS9DbGlja0hvdXNlL2NsaWNraG91c2UtZ28vdjIvbGliL2NvbHVtbi5FbnVtCj1naXRodWIuY29tL0NsaWNrSG91c2UvY2gtZ28vcHJvdG8uKCpDb2xEYXRlVGltZSkuRGVjb2RlQ29sdW1uChBuZXQuKCpjb25uKS5SZWFkCkVnaXRodWIuY29tL0NsaWNrSG91c2UvY2xpY2tob3VzZS1nby92Mi9saWIvY29sdW1uLigqU3RyaW5nKS5BcHBlbmRSb3cKFHJlZmxlY3QudHlwZWRtZW1tb3ZlChJyZWZsZWN0LnVuc2FmZV9OZXcKD3N0cmluZ3MuQ29tcGFyZQoRbmV0LigqbmV0RkQpLlJlYWQKFHJ1bnRpbWUudHlwZWRtZW1tb3ZlCgxzeXNjYWxsLlJlYWQKDHN5c2NhbGwucmVhZAoUcnVudGltZS5lbnRlcnN5c2NhbGwKFnJ1bnRpbWUucmVlbnRlcnN5c2NhbGwKRGdpdGh1Yi5jb20vQ2xpY2tIb3VzZS9jbGlja2hvdXNlLWdvL3YyL2xpYi9jb2x1bW4uKCpJbnQ2NCkuQXBwZW5kUm93ChtydW50aW1lLmVudGVyc3lzY2FsbF9zeXNtb24KHHJ1bnRpbWUuKCptaGVhcCkuYWxsb2MuZnVuYzEKGnJ1bnRpbWUuKCptaGVhcCkuYWxsb2NTcGFuChlydW50aW1lLigqbWhlYXApLmluaXRTcGFuCh5ydW50aW1lLigqbW9kdWxlZGF0YSkudGV4dEFkZHISEQoPAICy9fYGAACApd6KCAAAEtEBCs4BAIDokiYAAIDQpUwAAQCA0KVMAACA18xaAAIAgNrECQAAgLSJEwADAIDh6xcAAICcnDkABACA5Zp3AACA0KVMgK3iBAUAgNrECQAAAAAGAAAAAICOzhwABwCAreIEAAAAAAgAAAAAgK3iBAAJAIDaxAkAAICHpw4ACgCAtIkTAACA2sQJAAsAgOeI2AMAAID11vQDAAwAAAAAgK3iBAANAICt4gSAreIEAAAADgAAAACAreIEAA8AgISvXwAAgPPokwEAEACAreIEAAAAABESjAIKiQIAgOiSJgAAgNClTAASAIDQpUwAAIDXzFoAEwCA2sQJAACAtIkTABQAgOHrFwAAgJycOQAVAID9h1EAAIDvuTQAFgCA4esXAACAh6cOABcAgIenDgAAgK3iBAAYAICt4gSAreIEgK3iBAAAGQCAreIEAAAAABoAAAAAgI7OHAAbAICt4gQAAAAAHAAAAACAreIEAB0AAAAAgK3iBAAcAIDaxAkAAICt4gQAHgAAAACAreIEAB8AgLSJEwAAgNrECQAgAICt4gQAAAAAIQCAuqbTAwAAgPXW9AMAIgAAAACAreIEACOAreIEAAAAgK3iBAAkAICEr18AAIDz6JMBACUAgK3iBAAAAAAmEr0DCroDAICt4gQAAAAAHACAu7AhAACA0KVMACcAgLSJEwAAgOiSJgAoAAAAAICt4gQAKQCAreIEAACAreIEACoAgIenDgAAgIenDgArAICHpw4AAIDaxAkALACA4esXAACAtIkTAC0AgK3iBAAAgNrECYCt4gQuAICt4gSAreIEAIDaxAmA2sQJLwAAAACAreIEgK3iBDAAAAAAgK3iBAAxAICt4gQAAAAAMgCAreIEAACAtIkTADMAgNrECQAAgIenDgA0AICt4gQAAICHpw4ANQCA/YdRAACA77k0ADYAgOHrFwAAgIenDgA3AICt4gQAAAAAOACA2sQJAACAreIEgK3iBDaAreIEgK3iBACAreIEAAA5AAAAAICOzhwAOgCAreIEgK3iBAAAADsAAAAAgK3iBAAeAAAAAICt4gSAreIEPACA2sQJAACAreIEAD0AAAAAgK3iBAA+AIC0iRMAAIDaxAkAPwCAreIEAAAAAEAAgPDw5AEAAICMjZ4CAEEAgMq17gEAAIDpydYBAEIAAAAAgK3iBABDgK3iBAAAAICt4gQARACAhK9fAACA8+iTAQBFAICt4gQAAAAARhLSBArPBACAreIEgK3iBAAAAEcAgLuwIQAAgNClTABIAAAAAICt4gQASQCAtIkTAACAu7AhAEoAAAAAgK3iBABLAICt4gQAAICt4gQATACAh6cOAACAh6cOAE0AgIenDgAAgNrECQBOAAAAAICt4gQATwAAAACAreIEgK3iBFAAgNrECYCt4gQAAABRAICt4gQAAAAAUgCAreIEAACAreIEAFMAgK3iBAAAAABUAAAAAICt4gQAVQCAreIEgK3iBAAAAFYAAAAAgK3iBABXgK3iBAAAgLSJE4Ct4gQAWACAreIEAAAAAFkAAAAAgLSJEwBaAICt4gQAAAAAWwCA2sQJAACAh6cOAFwAgK3iBAAAAABdAAAAAICt4gSAreIEXgAAAACAreIEAF8AAAAAgK3iBABgAIDaxAkAAAAAYQCAreIEAAAAAGIAgPbgQgAAgO+5NABjAICt4gQAAICt4gQAZACAtIkTAACA2sQJADYAgK3iBAAAAABlAIDaxAmAreIEAAAAY4Ct4gSAreIEAIDaxAkAAGYAAAAAgK3iBABnAAAAAIDh6xcAaICt4gQAAACAreIEAGkAgNrECQCAreIEgK3iBABqAAAAAICt4gQAawCAreIEAACA2sQJAGwAgNrECQAAAABtAICt4gQAAAAAbgCAreIEAAAAAG8AgPDw5AGAtIkTAICMjZ4CgK3iBHAAgMq17gGAu7AhAIDpydYBgOHrF3AAAAAAgK3iBABZgK3iBAAAAICt4gQAKwCAhK9fAACA8+iTAQBxAICt4gQAAAAAchKnBwqkB4Ct4gSAjs4cAACA0KVMAHMAgK3iBAAAAAB0AAAAAICt4gQAdQCAh6cOAACAreIEAHYAAAAAgI7OHAB3AICt4gQAAAAAeAAAAACAreIEAHkAAAAAgK3iBAB6AICt4gQAAAAAZwCAh6cOAACAh6cOAHsAgIenDgAAgNrECQB8AAAAAICt4gQAWQCAreIEgK3iBICt4gQAAH2AreIEgK3iBAAAAAB+AICt4gQAAICt4gQAfwCAreIEAAAAAIABAAAAAICt4gQAgQGAreIEAAAAgK3iBACCAYCt4gQAAIC0iROAreIEAIMBAICt4gQAAAAAhAEAAAAAgIenDgCFAQAAAACAreIEAIYBAICt4gQAAAAAhwEAgNrECQAAgIenDgB5AICt4gQAAAAAiAEAAACAreIEgK3iBACJAQAAAACAreIEgK3iBIoBAIDaxAmA2sQJAAAAiwEAgK3iBAAAAABkAICt4gSAreIEAAAAjAEAgK3iBAAAAACNAQAAAACAreIEAI4BAIC0iRMAAIDaxAkAjwEAgIenDoCHpw4AgLSJE4Ct4gSQAQCAreIEgK3iBAAAAJEBAIC0iRMAAIC0iRMAkgEAgK3iBAAAgK3iBACTAQCAreIEAAAAAJQBAICHpw4AAIDaxAkAYwCAreIEAAAAAJUBAICt4gSAreIEAAAAlgGA2sQJgK3iBACA2sQJAACXAQAAAACAreIEAIQBAAAAAICHpw4AIgAAAACAreIEAJgBAAAAAICt4gQAmQGAreIEAAAAgK3iBABqAIDaxAkAgK3iBICt4gQAmgEAAAAAgK3iBACbAQCAreIEAACA2sQJAJwBAIDaxAkAAAAAagCAreIEAAAAAJ0BAICt4gQAAAAAngEAgIenDgAAgNrECQCfAQCAtIkTAACA4esXAKABAICt4gSAreIEAAAAoQEAAAAAgNrECYDaxAmiAQCAreIEgK3iBAAAAKMBAICn8qYBgMn+PQCAyrXuAYDQpUykAYC0iROAreIEAICt4gQAAKUBAICt4gSAreIEAAAApgEAAAAAgK3iBICt4gShAQCAmaSKAYDvuTQAgKDLmAGAnJw5pAEAgJX1KgAAgLSJEwCfAQCAh6cOAACA2sQJAKcBAAAAAICt4gSAreIEogGAu7AhAACA4esXgK3iBICt4gSEAYCt4gQAAACAreIEAE0AgKPDRwAAgL/fgAEAqAEAgOHrFwAAgLSJEwCpAQCAreIEAAAAAKoBEqwKCqkKgK3iBAAAAICHpw4AqwEAgI7OHAAAgMn+PQCsAQCAreIEAAAAAGgAAAAAgK3iBACtAQCAh6cOgK3iBACAreIEAK4BAAAAAICt4gQArwEAAAAAgK3iBACwAQAAAACAtIkTALEBAICt4gQAAAAAsgEAAAAAgK3iBACzAQAAAACAreIEgK3iBLQBAICt4gQAAAAAhAEAgIenDgAAgIenDgC1AQAAAACA2sQJALYBAICHpw4AAAAAtwEAAAAAgK3iBICt4gSEAYDaxAmAreIEgK3iBICt4gQAALgBAICt4gQAAICt4gQAuQEAgK3iBAAAAAC6AQAAAACAreIEgK3iBLsBgK3iBAAAAICt4gSAreIEvAGAreIEAACAtIkTgK3iBAC9AQCAreIEAAAAAL4BAAAAAICt4gQAvwEAAAAAgNrECQDAAQAAAACAreIEAMEBAICt4gQAAAAAwgEAgNrECQAAgIenDgCzAQCAreIEgK3iBAAAAF4AAACAreIEgK3iBACJAYDaxAmAreIEAICt4gQAAJMBgK3iBICt4gSAreIEAAAAwwEAAAAAgK3iBADEAQCAtIkTAACA2sQJAMUBAAAAAICHpw4AxgGAtIkTgLSJEwCAreIEgLSJEwDHAQCAreIEAACAreIEAMgBAICt4gSAreIEAAAAyQEAAAAAgK3iBABXAIDaxAkAAICt4gQAjwEAgK3iBICt4gQAAADKAQCAreIEAAAAAMsBgIenDoCt4gQAgNrECQAAzAEAAAAAgK3iBADNAQAAAACAh6cOAM4BAAAAAICt4gQAIgAAAACAreIEACKAreIEAAAAgK3iBACaAQCA2sQJAICt4gSAreIEAM8BAAAAAICt4gSAreIE0AEAgK3iBAAAgNrECQDRAQCA2sQJAAAAAJoBAICt4gQAAAAA0gEAgK3iBICt4gQAAADTAQCAreIEAAAAANQBAIDaxAkAAIDaxAkA1QEAgIenDgAAgOHrFwDWAQCAreIEAAAAANcBgNrECQAAgNrECYCt4gSAreIE2AEAgK3iBICt4gQAAADZAQCAreIEgK3iBACA2sQJgNrECdoBAAAAAICt4gSAreIE2wEAAAAAgNrECYDaxAncAQCAreIEgK3iBACAtIkTgLSJE90BAICt4gSAreIEAAAA3gEAAAAAgK3iBICt4gSiAQCAreIEAACAh6cOgNrECd8BAICHpw6Ah6cOAAAA4AEAgIenDoCHpw4AgLSJE4C0iRPhAQCA77k0gO+5NACA0KVMgNClTOIBgP2HUYCt4gSAreIEgP2HUQAA4wGAreIEgK3iBICt4gSAreIEAADeAQCAtIkTgLSJEwCA4esXgOHrF9oBAICt4gSAreIEAICt4gQA3wEAgK3iBICt4gQAgK3iBICt4gTZAQCA2sQJgNrECQCAreIEgK3iBOABAIDaxAmA2sQJAIC0iROAtIkT3QEAgNrECYDaxAkAgLSJE4C0iRPiAQCAreIEgK3iBAAAANsBAAAAAICt4gSAreIEowEAgNrECYDaxAkAgIenDoCHpw7hAQCA2sQJgNrECQAAAKIBgO+5NIDokiYAgJycOYDaxAkA1QEAgK3iBAAAgNrECQDUAQCA2sQJgNrECQCAreIEgK3iBOQBAICt4gQAAICt4gQA5QGA6JImAACAu7AhgK3iBAB7AICjw0cAAICS/XsA5gEAAAAAgK3iBADnAQCAtIkTAACAtIkTAOYBAICt4gQAAAAA6AEAgK3iBAAAAADpARLSBwrPB4Ct4gQAAACAh6cOgK3iBBQAgI7OHAAAgMn+PQDqAQCAreIEAAAAAJgBAAAAAICt4gSAreIE6wEAgNrECQAAgK3iBACuAYCt4gQAAACAreIEAFkAAAAAgK3iBABnAAAAAICt4gSAreIE7AEAAAAAgK3iBADtAQAAAACAreIEAO4BAAAAAICt4gSAreIE7wEAgK3iBAAAAADwAQAAAACAreIEAPEBAICt4gSAreIEgK3iBAAA8gEAgIenDgAAgIenDgDzAQAAAACA2sQJAPQBAICt4gSAreIEAAAA9QEAgK3iBAAAAAD2AQCAreIEAAAAAPcBgIenDoCt4gQAgNrECYCt4gQAhAEAgK3iBAAAAAD4AYDaxAkAAICOzhyAreIEAPkBAICt4gQAAAAA+gEAAAAAgK3iBAD7AQAAAACA2sQJAPwBAAAAAICt4gSAreIE/QEAgK3iBICt4gQAAAD+AQCA2sQJgK3iBACAh6cOAP8BgK3iBAAAgK3iBICt4gQAiQGA2sQJgK3iBACAreIEAADIAYDaxAkAAACAreIEgK3iBIACAIC0iRMAAIDaxAkAgQIAAAAAgIenDoCHpw6CAoC0iROAtIkTgLSJE4Ct4gSAtIkTgLSJE4MCAICt4gQAAICt4gQAhAKAreIEAAAAgK3iBACCAQCA2sQJAACAreIEgK3iBMUBgK3iBICt4gSAreIEAAAAhQKAh6cOgK3iBACA2sQJAACGAgAAAACAreIEAIcCAAAAAICHpw4AiAIAAAAAgK3iBACJAgAAAACAreIEAIoCgK3iBAAAAICt4gQAzwEAgNrECQCAreIEgK3iBACLAgCAreIEAICt4gSA2sQJAIwCAIDaxAkAAAAAzwEAgK3iBAAAAACNAoCt4gSAreIEAAAAAI4CAICt4gSAreIEAAAAjwIAgK3iBICt4gQAAADkAQAAAACA2sQJAJACAICHpw6A2sQJAIDh6xeA2sQJkQIAgK3iBAAAAACSAoCOzhyAreIEgK3iBIDJ/j0AAJMCAAAAAICt4gSAreIElAKAtcDDAQAAgOnJ1gGAreIEgK3iBJQCgLi4coC7sCGAu7AhgJL9e4Ct4gSAreIE5AEAgK3iBICt4gQAAACPAgAAAACAreIEAJACAICt4gQAAIDaxAkAjgKA2sQJgK3iBICt4gSAreIEgK3iBICt4gTDAYDokiYAAIC7sCGAreIEALUBAICjw0cAAICS/XsAlQIAAAAAgK3iBACWAgCAtIkTAACAtIkTAJUCAICt4gQAAAAAlwIAgK3iBAAAAACYAhLABgq9BoCt4gQAAACA2sQJgK3iBC4AgLSJEwCAreIEgJX1KgCZAgAAAACAreIEAJoCAICt4gQAAICHpw6AreIEmwIAgK3iBICt4gQAAACcAgCAreIEAAAAACIAgNrECQCAreIEgK3iBACdAoCt4gQAAACAreIEAIQBAAAAAICt4gSAreIEhAEAAACAreIEgK3iBABZAAAAAICt4gQAXwCAreIEAICt4gQAAJ4CAAAAAICt4gQAnwKAreIEgIenDgCAreIEgIenDgCgAgAAAACA2sQJAKECgK3iBICt4gQAAAAAogIAgK3iBAAAAACjAoCHpw6AreIEAIDaxAkAAL4BAAAAAICt4gSAreIEpAIAgK3iBICt4gQAAAClAoDaxAkAAICOzhyAreIEAKYCAICt4gSAreIEAAAApwIAAAAAgK3iBICt4gSKAQAAAACAreIEAKgCAAAAAICt4gQAqQKAreIEgK3iBACAreIEgIenDgCqAoDaxAkAAICt4gSAreIEAIkBgNrECYCt4gQAgK3iBAAAhAKA2sQJgLSJE4C0iROAreIEgNrECYDaxAmrAoDokiaAreIEgK3iBIDokiaAreIEgK3iBKsCgK3iBAAAAICt4gSAreIEvAEAgNrECQAAAACBAoDh6xeAreIEAICHpw4AAKwCAAAAAICt4gQArQIAAAAAgIenDgCuAgAAAACAreIEAK8CAAAAAICt4gQAsAKAreIEAAAAgK3iBACLAgCA2sQJAICt4gSAreIEALECAICt4gQAgK3iBIDaxAkAsgIAgNrECQAAAACLAgCAreIEAAAAALMCgK3iBICt4gQAAAAAtAKA2sQJAAAAgNrECYDaxAnDAQCAreIEgK3iBAAAAOABAAAAAICt4gSAreIE2gEAAAAAgK3iBADZAQAAAACAreIEgK3iBNwBgNrECYCt4gQAgNrECQAAtQKAkLz9AgAAgMvsngOAreIEgK3iBMMBAICt4gQAAIDaxAmAreIEtAKA77k0AACAlfUqgK3iBACgAgCAo8NHAACAkv17ALYCAAAAAICt4gQAtwIAgLSJEwAAgLSJEwC4AgCAreIEAAAAALkCAICt4gQAAAAAugISuAUKtQWAreIEAAAAgK3iBICt4gS7AgCAreIEAIDaxAmA2sQJALwCAICHpw4AAICOzhwAvQIAAAAAgK3iBAC+AgAAAACAreIEAL8CAICt4gQAAIDaxAkAwAKAreIEgK3iBACAreIEAACJAgCAreIEAICt4gQAAMECAICt4gSAreIEAICt4gQArgGAreIEAAAAgK3iBADCAgAAAIDaxAmAreIEgK3iBIQBAAAAAICt4gQAiQEAgK3iBACAreIEAADDAgAAAACAreIEAMQCgK3iBICHpw4AgK3iBICHpw4AxQIAAAAAgNrECQDGAoCt4gSAreIEAAAAAMcCAICt4gQAAAAAZ4CHpw6AreIEAIDaxAkAAPoBgIenDgAAgLuwIYCt4gQAmAKAreIEAACAreIEgK3iBICt4gTIAgAAAACAreIEgK3iBPwBgK3iBICt4gQAgK3iBICHpw4AyQKA2sQJAACAreIEgK3iBADKAoDaxAmAreIEgK3iBICt4gQAAKsCgNClTIDaxAmA2sQJgMn+PQAAqwKA4esXgK3iBACAh6cOAADLAgAAAACAreIEAMwCAAAAAICHpw4AzQIAAAAAgK3iBICt4gTOAgAAAACAreIEAM8CgK3iBAAAAICt4gQAsQIAgNrECQCAreIEgK3iBADQAgCAreIEAICt4gSA2sQJANECAIDaxAkAAAAAsQIAgK3iBAAAAADSAoCt4gSAreIEgK3iBAAAANMCgIenDgAAgIenDoCt4gQA1AKA2sQJgK3iBACAh6cOAACRAoCQvP0CgK3iBICt4gSA+M6jA4Ct4gSAreIE0wKA77k0AACAwtcvgK3iBADFAgCAo8NHAACAkv17ANUCAAAAAICt4gSAreIE1gIAgLSJEwAAgLSJEwDXAgCAreIEAAAAANgCAICt4gQAAAAA2QISgwUKgAWAreIEAACAh6cOgK3iBABZAICt4gQAAAAA2gIAAAAAgK3iBADbAgCAreIEAACAjs4cANwCAICt4gQAAAAA3QIAgK3iBAAAAADeAgAAAACAreIEANoCAAAAAICt4gQA3wIAgK3iBAAAgNrECQDgAoCt4gSAreIEAICt4gQAAK8CAICt4gQAgK3iBAAA4QIAAAAAgK3iBICt4gSdAoDaxAkAAACAreIEgK3iBOICAAAAgIenDoCt4gQAygIAgK3iBICt4gSAreIEAADjAgAAAACAreIEAOQCgK3iBICt4gQAgK3iBAAA5QIAgNrECQAAAADmAgAAAACAreIEAOcCAAAAAIDaxAkAmAIAAAAAgNrECQDoAoCt4gSAreIEAAAAAOkCAICt4gQAAAAAhAGAh6cOgK3iBACA2sQJAADpAoCHpw4AAIC7sCGAreIEALoCgNrECYCt4gQAgLSJE4CHpw6AreIE6gKA2sQJAACAreIEgK3iBICt4gTrAoCS/XuAreIEAID9h1EAAOwCAAAAAICt4gSAreIE7QIAAAAAgK3iBADuAgAAAACA2sQJAO8CAAAAgK3iBICt4gQA8AKAreIEAAAAgK3iBADQAgCA2sQJgNrECYCt4gSAreIEgK3iBIMCAICt4gQAgK3iBIDaxAkA8QIAgNrECQAAAADQAgCAreIEAAAAAPICgOHrFwAAgIenDoCt4gQA1AGA2sQJgK3iBACAh6cOAADzAoCs2LYDAACA54jYA4Ct4gQA5gIAgI7OHAAAgPbgQgD0AgCAlfUqAACAnJw5APUCAIC0iRMAgK3iBIC0iRMA9gIAgK3iBAAAAAD3AgCAreIEAAAAAPgCEskFCsYFgK3iBAAAgIenDoCt4gSAreIE+QIAgK3iBAAAAAD6AgAAAACAreIEAPsCAICt4gSAreIEAICt4gSAreIE/AIAAAAAgOHrFwD9AgCAreIEgK3iBAAAAP4CAICt4gQAAAAAlQEAAAAAgK3iBAD6AgAAAACAreIEAP8CAICt4gQAAIDaxAkAgAOAreIEgK3iBACAreIEAADIAQCAreIEAICt4gQAAIEDgNrECQAAgOHrF4Ct4gSAreIEggOAreIEAACAreIEgK3iBACDA4Ct4gSAreIEgK3iBICt4gQAAIQDAICt4gQAAAAAhQMAgK3iBAAAAABnAAAAAICt4gQAhgMAAAAAgK3iBAC6AgAAAACAreIEAIcDAAAAAICt4gSAreIEiAMAAAAAgK3iBACJA4Ct4gSAreIEAAAAACIAgK3iBAAAAAC+AYCHpw6AreIEAIDaxAkAACKAh6cOAACAu7AhgK3iBACKA4DaxAmAreIEgK3iBIC0iROA2sQJgK3iBIsDgOzBhQGAreIEAICEr18AAIwDAAAAgK3iBICt4gSAreIEjQMAAAAAgNrECYCt4gSOAwAAAICt4gSAreIEAIECgK3iBAAAAICt4gSAreIEgwKA2sQJgK3iBACAh6cOgNrECQCPAwCA2sQJgNrECQAAAIMCAICt4gSAreIEAAAAgwKA4esXAACAh6cOgK3iBACOAoDaxAmAreIEgK3iBICHpw4AAOQBgKzYtgMAAIDniNgDgK3iBACQAwCAjs4cAACA9uBCAJEDAIC0iRMAAIDh6xcAkgMAgNrECQAAgIenDgCTAwCAreIEAAAAAJQDAIDaxAkAAIC0iRMAlQMAgK3iBACAreIEgNrECQCWAwAAAACAreIEAJIDAICt4gQAAAAAlwMAgNrECQAAgK3iBACYAwCAreIEAAAAAJkDAICt4gQAAAAAmgMStQUKsgWAreIEgK3iBICt4gSAtIkTAACbAwAAAACAreIEAGeAreIEAACAreIEgOHrF4Ct4gT/AoCt4gSAreIEgK3iBAAAAMsBAAAAAICt4gSAreIEnAMAAAAAgK3iBICt4gSdAwCAreIEgK3iBAAAAJ4DAAAAAICt4gQAnwMAAAAAgK3iBICt4gSgA4Ct4gSAreIEAICt4gQAAIQCAICt4gSAreIEgK3iBAAAoQOAh6cOAACAu7AhgK3iBICt4gSiA4DaxAmAreIEgK3iBICt4gQAAKMDAICt4gQAAAAAhAEAAAAAgK3iBACkAwAAAACAreIEANkCAAAAAICt4gQApQMAAACAreIEgK3iBACmA4Ct4gSAreIEAAAAAKcDAICt4gQAAAAA+gGAh6cOgK3iBACA2sQJAACnA4CHpw4AAIC7sCGAreIEgK3iBKgDgNrECQAAgLSJE4Ct4gSAreIEqQOAmaSKAYCt4gQAgLGRZAAAqgMAAACA2sQJgK3iBICt4gS7AgAAAIDaxAmAreIEgK3iBKsCgIenDoCt4gQAgLSJE4Ct4gQAqwMAAAAAgK3iBACsA4DokiYAAICHpw6AreIEALQCgLP/xAMAAIDur+YDgK3iBICt4gStAwCA4esXAACAu7AhAK4DAICt4gQAAIC7sCEArwMAAAAAgNrECQCwAwAAAACAreIEALEDAIC0iRMAAIDaxAkAsgMAgK3iBAAAgNrECYCt4gSzAwCAreIEAACAreIEALQDAICt4gQAAAAArAIAgNrECQAAgLSJEwC1AwCAreIEAICt4gSA2sQJALYDAAAAAICt4gQAsAMAgK3iBAAAAAC3AwCAreIEAAAAALcDAICt4gQAAAAAuAMAAAAAgK3iBICt4gS5AwCAreIEAAAAALoDAICt4gSAreIEAAAA/wISkQQKjgSA2sQJAACAtIkTgK3iBACEAYCt4gQAAICt4gSAh6cOgIenDp0DAAAAAICt4gSAreIEuwOAh6cOAACAh6cOgK3iBAC8A4Ct4gSAreIEgK3iBIDaxAkAAKsCgLuwIYCt4gQAgMLXLwAAzQEAAAAAgK3iBAC9AwAAAACAreIEgK3iBPgCAAAAAICt4gQAvgMAAACAreIEgK3iBAC/A4Ct4gSAreIEAAAAAMADAICt4gSAreIEAAAApwKAh6cOgK3iBACA2sQJAADAA4D6j6IBgK3iBACA+o+iAQAAwQOAh6cOgK3iBACAwtcvgK3iBADCAwAAAACAreIEAMMDgOiSJgAAgIenDoCt4gSAreIE0wKAs//EA4Dh6xcAgJuS6wOAjs4cAMQDAAAAAICt4gQAxQMAgK3iBAAAgOHrFwDGAwAAAACA2sQJAMUDAAAAAIDaxAkAxwMAAAAAgK3iBADIAwCAtIkTAACA2sQJAMkDAICt4gQAAICt4gSAreIEygMAgK3iBACAreIEgK3iBADLAwCAreIEgK3iBAAAAMsCAAAAAICt4gQAzAMAgNrECQAAgK3iBADNAwAAAACA2sQJAM4DAICt4gQAgK3iBIDaxAkAzwMAAAAAgK3iBADQAwCAreIEgK3iBAAAAIQBAICt4gQAAAAAhAEAgK3iBAAAAADRAwCAreIEAICt4gQAANIDEu4DCusDgNrECQAAgLSJE4Ct4gQAwgKAtIkTAACA6JImgK3iBADTA4CV9SqAreIEAICcnDkAAIcCAAAAAICt4gQAIgAAAICt4gSAreIEgK3iBNQDAAAAgK3iBICt4gSAreIE+QKAreIEgK3iBAAAAAC/AoC0iROAreIEAIDaxAkAAIoDgPqPogGAreIEAID6j6IBAACJA4CHpw6AreIEAIDC1y+AreIEANUDAAAAAICt4gQA1gOAm5LrAwAAgM+b/gOA2sQJANcDAIDaxAkAAAAA2AMAgIenDgAAgLSJEwDZAwAAAACAreIEANoDAAAAAIDaxAkA1wMAgK3iBAAAgNrECQDZAwAAAACAreIEAFkAAAAAgK3iBADaAwAAAACAreIEgK3iBNsDAAAAAIDaxAkA3AMAAAAAgK3iBADdAwCAreIEgK3iBAAAAN4DAIDaxAkAAIDaxAkA3wMAgK3iBAAAAADgAwCAreIEgK3iBAAAAOMCAICt4gQAgNrECYCt4gQA4QOAreIEAAAAgK3iBADiAwCA2sQJAACAreIEALADAAAAAIDaxAkA4AMAgK3iBACAreIEgNrECQDjAwAAAACAreIEAGqAreIEgK3iBICt4gQAAACkAgCAreIEAAAAAOQDAICt4gQAgK3iBAAAmAIS2wMK2AOA2sQJAACAtIkTgK3iBICt4gTlA4C0iRMAAIDokiaAreIEgK3iBOYDgJX1KoCt4gQAgJycOQAAjgMAAAAAgK3iBADnA4Ct4gSAreIEgK3iBIC0iRMAAH2AtIkTgK3iBACA2sQJAAC+A4D6j6IBgK3iBACA+o+iAQAApgOAh6cOgK3iBACAwtcvgK3iBADoAwAAAACAreIEAOkDgJuS6wMAAIDPm/4DgK3iBABnAAAAAICt4gQA6gMAgNrECQAAAADrAwAAAACAreIEAFkAgIenDgAAgIenDgDsAwAAAACAreIEALkBAAAAAICt4gSAreIE7QMAAAAAgK3iBICt4gTaAgCAreIEAACA2sQJAOwDAAAAAICt4gSAreIE7gMAAAAAgK3iBADvAwAAAICt4gSA2sQJgK3iBPADAAAAAICt4gSAreIE8QOAreIEgNrECQAAgNrECQDyAwCAreIEAAAAAPMDgK3iBICt4gQAgNrECYCt4gQAWYCt4gQAAACAreIEAPQDAIDaxAkAAICt4gQA0AMAAAAAgNrECQDzAwCAreIEAICt4gSA2sQJAPUDAAAAAICt4gQAmgGA2sQJgK3iBICt4gQAAAD2AwCAreIEAICt4gQAALoCEoQDCoEDgKPDR4Ct4gSAreIEgJL9ewAAuwIAAAAAgK3iBAD3A4COzhyAreIEgK3iBICOzhwAAPgDgPqPogGAreIEgK3iBID6j6IBAAD9AYCHpw6AreIEAIDC1y+AreIEANACAAAAAICt4gQA+QOAm5LrAwAAgM+b/gOAreIEAIQBAAAAAICt4gQA+gMAgNrECYCt4gQAAAD7AwAAAACAreIEgK3iBPIBAICHpw4AAICHpw4A/AMAAAAAgK3iBICt4gR9AICt4gQAgNrECYDaxAkA/AMAAACAreIEgK3iBAD9AwAAAICt4gSAreIEgK3iBP0BgK3iBAAAgNrECYCt4gQA/gMAgNrECQAAgK3iBAD/AwCAreIEAAAAAIAEgK3iBICt4gQAgNrECYCt4gSAreIEhAGAreIEAAAAgK3iBACBBACA2sQJAACAreIEAGoAAAAAgNrECQCABACAreIEAICt4gSA2sQJAIIEAAAAAICt4gQAzwGAh6cOgK3iBACAreIEAADZAhK8Agq5AoDQpUwAAICS/XuAreIEAGSA6cnWAYCt4gSAreIEgMq17gGAreIEgK3iBIMCAAAAAICt4gQA8gKAm5LrAwAAgM+b/gOAreIEAMICAAAAAICt4gSAreIEgwQAgK3iBICt4gQAAACEBICt4gSAh6cOAICt4gSAh6cOAIUEAAAAgIenDoCt4gQAhgQAgK3iBAAAgK3iBACFBAAAAICt4gSAreIEAIcEgK3iBAAAgLSJE4Ct4gQAiAQAgNrECQAAgK3iBACJBACAreIEAAAAAIoEgK3iBICt4gSAreIEgNrECQAAiwSAreIEAACAreIEgK3iBACMBACA2sQJAACAreIEAJoBAAAAAIDaxAkAigQAgK3iBACAreIEgNrECQCCBAAAAACAreIEAIsCgIenDoCt4gQAgK3iBAAA+AISvAIKuQKA0KVMAACAkv17gK3iBICt4gSNBICWrNsBAACA95fzAYCt4gSAreIEgwKAm5LrAwAAgM+b/gOAreIEgK3iBOICgNrECYCt4gSAreIEgNrECQAAjgQAAAAAgK3iBACPBACA2sQJAACAreIEAJAEAAAAAICt4gSAreIEkQQAAACAh6cOgK3iBACSBAAAAACAreIEAJMEAICt4gQAAAAAkQQAAACAreIEgK3iBACUBICt4gQAAIC0iROAreIEAJUEAIDaxAkAAAAAlgQAAAAAgK3iBACXBACAreIEAAAAAJgEgIenDgAAgIenDoCt4gSAreIEmQQAgNrECQAAgK3iBADPAQAAAACA2sQJAJgEAICt4gQAgK3iBIDaxAkAmgQAAAAAgK3iBACxAoCHpw6AreIEAICt4gQAAJoDEvYBCvMBgIiLoQYAAIC5nIUHgK3iBACbBACA2sQJAACAreIEAJwEAAAAgLSJE4Ct4gQAnQQAAAAAgK3iBICt4gSeBACAreIEAAAAAJwEAAAAgK3iBICt4gSAreIEnwSAreIEAACAtIkTgK3iBICt4gSgBACA2sQJAAAAAKEEAAAAAICt4gQAogQAgK3iBAAAAACjBICHpw6A2sQJAIC0iROAreIEAIsCAAAAAIDaxAkAowQAAACAreIEgK3iBADhAwCAreIEAAAAAM8DAAAAAICt4gSAreIEpAQAAAAAgK3iBADQAoCHpw6AreIEgK3iBICt4gQAAP8CEsUBCsIBgIiLoQYAAIC5nIUHgK3iBICt4gSbAwCAreIEAACAreIEAKUEAICt4gQAAAAApgQAAACAtIkTgK3iBACnBACAreIEAICt4gQAAKYEgK3iBICt4gQAgLuwIQAAZwCAreIEAAAAAFkAAAAAgK3iBABZAICt4gQAAAAAqASAh6cOgNrECQCAtIkTgK3iBACxAgAAAACA2sQJAKgEAAAAgK3iBICt4gQAWQCAreIEAAAAAOMDAAAAgK3iBICt4gSAreIEgwISvAEKuQGAiIuhBoCt4gQAgOb+iQeAreIEgK3iBKkEAICt4gSAreIEAAAAhAEAAACAtIkTgK3iBICt4gSfBACAreIEgK3iBICt4gQAAPkCgK3iBICt4gQAgLuwIQAAhAEAgK3iBICt4gQAAAD5AgAAAACAreIEAIQBAICt4gQAAAAAqgSAh6cOgNrECQCAtIkTgK3iBADQAgAAAACA2sQJAKoEAAAAgK3iBICt4gSAreIE+QIAgK3iBAAAAAD1AxJ1CnOAiIuhBoCt4gSAreIEgOb+iQcAAP0BgIenDoCt4gQAgPbgQgAAvgGAreIEAAAAgK3iBADNAQCAreIEAAAAAKsEgIenDoDaxAmA2sQJgLSJE4Ct4gSAreIEgwIAAAAAgNrECQCrBACAreIEAIDaxAkAAIIEEk0KS4C8lLQGgK3iBACA3N/MBwAA+gGAreIEAAAAgK3iBACHAgCAreIEAAAAALECgOHrFwAAgOHrF4DaxAkAsQIAgK3iBACA2sQJAACCBBJYClaAvJS0BoCt4gQAgNzfzAcAAOkCgK3iBAAAAICt4gQArQIAgK3iBAAAAACsBIDh6xcAAIDh6xeAreIEANACAAAAAICt4gQArAQAgK3iBACA2sQJAACaBBJdCluAvJS0BoCt4gQAgNzfzAcAACKAreIEAAAAgK3iBADMAgCAreIEAAAAAK0EgOHrFwAAgOHrF4Ct4gSAreIEgwIAAAAAgK3iBACtBACAreIEgK3iBIDaxAkAAK4EEj0KO4C8lLQGgK3iBACA3N/MBwAApwOAreIEAAAAgK3iBADtAgCAreIEAAAAACKA4esXAACAjs4cgK3iBAAiEj4KPIC8lLQGgK3iBACA3N/MBwAAwAOAreIEAAAAgK3iBAAiAICt4gQAAAAArwSA4esXAACAjs4cgK3iBACvBBI/Cj2AvJS0BoCt4gQAgNzfzAcAAIoDgK3iBAAAAICt4gQAsAQAgK3iBAAAAADIAYDh6xcAAICOzhyAreIEAMgBEj8KPYC8lLQGgK3iBACA3N/MBwAAvgOAreIEAAAAgK3iBACxBACAreIEAAAAAIQCgOHrFwAAgI7OHICt4gQAhAISSApGgLyUtAaAreIEAIDc38wHAADUA4Ct4gQAAACAreIEgK3iBLIEAICt4gSAreIEAAAAqwKA4esXAACAjs4cgK3iBICt4gSrAhIYChaAvJS0BoCt4gSAreIEgNzfzAcAALMEGIDX04EPIIDQpUwogLL19gYwgKXeigg="; - let l_b_req = base64::decode(l_b64_req).unwrap(); - let r_b_req = base64::decode(r_b64_req).unwrap(); - let b_resp = base64::decode(b64_resp).unwrap(); - merge_tree(0, l_b_req.as_slice(), "cpu:nanoseconds".to_string()); - merge_tree(1, r_b_req.as_slice(), "cpu:nanoseconds".to_string()); - let res = diff_tree(0, 1, "cpu:nanoseconds".to_string()); - let o_res = FlameGraphDiff::decode(res.as_slice()).unwrap(); - let exp_res = FlameGraphDiff::decode(b_resp.as_slice()).unwrap(); - let mut res_names = o_res.names.clone(); - res_names.sort(); - let mut exp_names = exp_res.names.clone(); - exp_names.sort(); - assert_eq!(res_names, exp_names); - assert_eq!(o_res.levels.len(), exp_res.levels.len()); - for i in 0..o_res.levels.len() { - let mut exp_level: Vec<String> = Vec::new(); - let mut res_level: Vec<String> = Vec::new(); - for j in 0..exp_res.levels[i].values.len() / 7 { - exp_level.push(exp_res.names[exp_res.levels[i].values[j * 7 + 6] as usize].clone()) - } - for j in 0..o_res.levels[i].values.len() / 7 { - res_level.push(o_res.names[o_res.levels[i].values[j * 7 + 6] as usize].clone()) - } - exp_level.sort(); - res_level.sort(); - exp_level.insert(0, "level: ".to_string() + i.to_string().as_str()); - res_level.insert(0, "level: ".to_string() + i.to_string().as_str()); - assert_eq!(exp_level, res_level); - } - assert_eq!(o_res.levels.len(), exp_res.levels.len()); - } -} - -const TEST_PPROFS: &str = r#"HEADERS: -User-Agent: Go-http-client/1.1 -Content-Length: 1866 -Content-Type: multipart/form-data; boundary=43ba238906960207d409d77db17b9ae9fbca49492081293d389a31f4c7f5 -Accept-Encoding: gzip -BODY: -LS00M2JhMjM4OTA2OTYwMjA3ZDQwOWQ3N2RiMTdiOWFlOWZiY2E0OTQ5MjA4MTI5 -M2QzODlhMzFmNGM3ZjUNCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsg -bmFtZT0icHJvZmlsZSI7IGZpbGVuYW1lPSJwcm9maWxlLnBwcm9mIg0KQ29udGVu -dC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0KH4sIAAAJbogE/7ST -PWgc1xbH38zurK52Leta/tB4rYf3zePxhoW9d/dqtbvqHq95TyaF3ZkYM56592r2 -SjP3LDN3hORqCqtJkRgCiYuYuDYuYgIJwUQRxk4TQ4qEBBxC0qlwKjeJP2ADTsDg -Ip27wyn+55zfj/NmFVnYvlSWrXoVVXC1XkUOtutVVHtRz2Dbm0eW++CLt//lVZGF -T2GrXUcWRi5qzfonPIxs9/un79qeg2z8bR9b7Qaycd2ttxr+p31vEVXcb767ansO -quCP+56DqviJha12A1XwIfdQq+Hf7rcbqIrn3LlWw79qLTQX6la5f61mlfvXavWK -ZVeaM8h2y/1rNQ+jqvv+o/uznoMcfKeCrXYdOfiwe7g17896GDnulw/uz3oOquE7 -FWy166iGsYtb8/7sgruA0M1dD93c9erVqmNXmjVku2VZ9TCquQ8/u2J7DprBzy1s -tRtoBh9xj7QW/F8tD6MZ98pvV23PQQj/5GCr3UAIH3WPthr+J46HEXJ/eO+67Tlo -Fv9oY6vdQLP4mHusddz/0FpYXED2rae+feupX6/UZlCzhmy33Mf///rzr3avf/Dw -yWLzOLJw+XNpu2X5iLTKS/6J7uLIajaRjcvyrbt7B0tuWf5yd+9gqVWWdd/tnmye -RBVc7l+5t3ew5JY33rm3d7DUKlt+s3uqeQxVcbn/0ePn09NuWd5+/Hx62l9qNpGD -y7J8Np1Op1PLLW+Uz6bT6XRq+X9nf2NOPgm5ZE60Y2TO5sIkAR5AtCG5yZnDodCG -NcIkAR7kk5BLNqd0kcsAog3JTc4aShe5DPJJyCXrxsqMi4hwSGkMnO/QGDobOWiq -tJGZDhMqJAchM6K0MqTLAjqGVNJxBilclpshPXd+7SwFI5MOhySR3EBGt6QWkNFY -mXEREQ4pjYHzHRpDZyMHTZU2MtNhQoXkIGRGOaQTlUgSA5vPCm1UKomANa1MjwV0 -DKmk4wxSuCw3QxoDnWzGNAVBY0hCHRPIYmoAEj4Olf7PVpd0Sa8TQ4+wHlkmidLF -didMxaBP84zTrNBGpZJOMuAkBnY4K7RRqSQC1rQy7FBWaKNSSdJQafbvWJlxEREO -Kd0WcScGmptM6XiSyQlJwwlRWhnC2Dk6hlTScQYpXJabIT13fu0sBSOTDockkdxA -RrekFpDRWJlxEREOKd0WcScGmptM6XiSyQk1YZTInMTA/hErMy4iwiGl2yLuxEBz -kykdTzI5eWGEHckKbVQqiTJhlCutDLv0uoCp9ZBLEsPLoTkfS6G0Mi8hZqYbxMD4 -61oizNPghUuSs13r1VMJD/lY0jPS/DcLlc7pmjYySdSZNSFD1mV90qMmndD/wRuh -FjQIgl4MQVSoRAR//ELAIQ1SaTLFIQAjk4BDkkhuIAt4Kl5tTXYyYH53dTAcDPlw -eaXbE2I4XBFiNJAy6rGICdGVjPGQ9/iQ/ZMWeUYTFdHt0SAY9DuJ0sV2J9YFTVTE -SQ5kwPyBGPSj3nA9CvlwdVUORiIcykisro5WhFhfjVZ4tDxckQNG/iJO/Jm9PRp0 -Bn2SA2HMX15Zlr1owKOulBHvjvj68vr6qMdkKEfhSi/qy3UxGPUlq13YEjlcZPUL -W/lOzsMkufj7AJtndH+ABgAADQotLTQzYmEyMzg5MDY5NjAyMDdkNDA5ZDc3ZGIx -N2I5YWU5ZmJjYTQ5NDkyMDgxMjkzZDM4OWEzMWY0YzdmNQ0KQ29udGVudC1EaXNw -b3NpdGlvbjogZm9ybS1kYXRhOyBuYW1lPSJzYW1wbGVfdHlwZV9jb25maWciOyBm -aWxlbmFtZT0ic2FtcGxlX3R5cGVfY29uZmlnLmpzb24iDQpDb250ZW50LVR5cGU6 -IGFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbQ0KDQp7ImFsbG9jX29iamVjdHMiOnsi -dW5pdHMiOiJvYmplY3RzIn0sImFsbG9jX3NwYWNlIjp7InVuaXRzIjoiYnl0ZXMi -fSwiaW51c2Vfb2JqZWN0cyI6eyJ1bml0cyI6Im9iamVjdHMiLCJhZ2dyZWdhdGlv -biI6ImF2ZXJhZ2UifSwiaW51c2Vfc3BhY2UiOnsidW5pdHMiOiJieXRlcyIsImFn -Z3JlZ2F0aW9uIjoiYXZlcmFnZSJ9fQ0KLS00M2JhMjM4OTA2OTYwMjA3ZDQwOWQ3 -N2RiMTdiOWFlOWZiY2E0OTQ5MjA4MTI5M2QzODlhMzFmNGM3ZjUtLQ0K -******************* -HEADERS: -User-Agent: Go-http-client/1.1 -Content-Length: 757 -Content-Type: multipart/form-data; boundary=f8b9e5464625dbb7eaca60174d56df842f385d760db9d6f978a8974c64ab -Accept-Encoding: gzip -BODY: -LS1mOGI5ZTU0NjQ2MjVkYmI3ZWFjYTYwMTc0ZDU2ZGY4NDJmMzg1ZDc2MGRiOWQ2 -Zjk3OGE4OTc0YzY0YWINCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsg -bmFtZT0icHJvZmlsZSI7IGZpbGVuYW1lPSJwcm9maWxlLnBwcm9mIg0KQ29udGVu -dC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0KH4sIAAAAAAAE/1yM -v2sUQRxHveSSrBeLBQXPJWDAZpvb2Z3bnd1tbTTBwtoQJvPju3eLszvHzm64WE2h -jQg2djap7AT9B0IIqe1NJYqk0ELsRCGSAxs/zefBg3f/y9mHV19fn/26Oeg7PXdp -0HeW3f7DNy8+/kgfLXjPvv3c9647Pdd+sktDa78Fm3bPXwlXPc9Zcq19fnJ0vjG0 -9vvJ0fnGprUDfy10vFvOsmuPn54u3OHL04Xb9K+GA++G03ft8buffy5uD619f/n+ -uuc5K6619vfF5XpDe/gP/Wv4Cl4zrJopMHhF6K5u8bKYdXi9ZrU2IHQtDX7WQ1Nd -AZo2utJP4DFDgWBiCmgb2rsNK2uDtuoWlCq3tyQwHOI4iFBbzdA9/YDVElFKo4mm -vCuVpJOynXacCl3RCtqmFJrqFhQVWikQrW6oqCTVLSgqtFIgWt3Q2UGjsR/mJCWp -SMdJGEmZpomUGQHgEeZYyhAwFkxEIsV3UGcapEqO5hmhJB6psu7mo0ndIVVyERgd -EOwTSWIepQVnIs1zIJlkKXCZ51kiZZHzRPBxmgDBAepMg1TJ0Twj/+XkSJV1Nx/N -MzIicWB0gLE/TsYQcSJ4CMBFmIliXBRZhIFBxpKIx1BIksWAV3f2pdG7eLCzbw6M -YErt/g0AAP//01WZ7zgCAAANCi0tZjhiOWU1NDY0NjI1ZGJiN2VhY2E2MDE3NGQ1 -NmRmODQyZjM4NWQ3NjBkYjlkNmY5NzhhODk3NGM2NGFiLS0NCg== -******************* -HEADERS: -User-Agent: Go-http-client/1.1 -Content-Length: 10325 -Content-Type: multipart/form-data; boundary=b522389a4e947d96573c381377b6c37631c08faa82e05d7fdd8382b88938 -Accept-Encoding: gzip -BODY: -LS1iNTIyMzg5YTRlOTQ3ZDk2NTczYzM4MTM3N2I2YzM3NjMxYzA4ZmFhODJlMDVk -N2ZkZDgzODJiODg5MzgNCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsg -bmFtZT0icHJvZmlsZSI7IGZpbGVuYW1lPSJwcm9maWxlLnBwcm9mIg0KQ29udGVu -dC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0KH4sIAAAJbogE/7x7 -e3wU5dV/zrKzOWwkOVlFHm4yPIpOFjJDBgjgrUIABRERVBTUONl9MhnZnVlnZhNi -bV2QiyIiqAgq3vGGaG19a7VXbG3BXmhrbdVqq23t25+1tWrtxWr19/nMbJJNAjEB -fP9Jdvc5zznfc3nOOc/zzCyNIlDk0kJBjkdxEEXjUZQoEo9iLPhcThFOCOzZ578T -4RIC/VEiSFYgEDKUBys/lHgVRtjzL90m8ShGKEGQjGOE4iwuVyjVnHAQ+9K+WyUu -4SDaGCFIVuAgOoIdIQ9Rro5wwih7Y882iUsYpQdiBMkKjFIlq5SrlPtjiaGJ2Px1 -t8tlZfEoRAZFR0gYYYV6XoUS+9djd0g8ihKdS5CMo0TESK5WFvIqjLEtL9wt8SjG -aFIAJ0YJlpCPVBKcsJw9t/YOiUtYTv8MVSmno9hR8lDltxInRHbTvXdIXEKkD2ME -yQpEOpodLQ9V/hnjw3Awe+8H90lcwsH0WjmXME63RgiSFTiYhrFh8lDlt+XJCowT -Y0werlwT4YRxdvvfd0hcwgr6boQgWYEVNIKNkEcquyK8CivYE7+4T+JRPIKsAOwR -NIqNkkcrjFfhEezj3zwg8SgOoXMIknEcQsewY+QxylROOIT99v0HJS5hJX05ZFxJ -MpPlsco9EU5YyVa/+6DEJayiOyIEyQqsIs64PFb5CDhhFbvpDw9KXEKif0EAi+hY -dqw8Vvk7cEJiT770oMQlrKbbgCBZgdV0HDtOHqvcDJywmj322oMSlzBBTwFBsgIT -NI6Nk8cqT0JiTCIW+ebq8WVl8SFSrBwHxyuOGFJZRdUjYhhhhcJRnDDBtj67TeIS -Hkm3hmY+ko5nx8tVytUxXoVHsnee3i7xKB5FwwO9j6IT2AmyogznhEexm3YGeg+l -n4fQhlINq5GTyh7gVTiU3b79QYlH8WjygrlH03g2Xk4qizjh0eyXvwnmDqPvhLiH -0QQ2QR6rfAsSwxMI21bNg22r5sWlxJFHDT16RAwjrFA4jQ/DYezH37lP4hIyurmc -SzicfgcEyQpkVMtq5aHKTeXJChxOKlPl4cojwAkZW//3HRKXsIKeiRAkRiZikV3/ -UcrK4oOHsW422U28CoezHb8uOvt0ggRLxBKr3pXLyuKx4SWkrwMfhiPYG3/eO4hL -OIJuk7iEIzu0GUEa0+SJyq1SsgJHUh2rk3XlOuCEI9nW3wbLcBStHRRoPoomsUny -EOXVCCccxVY/V1yGX4sRJIYmYvDS/0wrK4sPGjFyVCh6t8KrcDR79taieS8iSAxL -xGDbqnllZYHFRndZ7Gg8hl17zepaHsXRNJ1LeAzdHgn8MZoms8nyFEVLVuAxVM/q -5anKbRFehWPY3h2/reVRHEOLCZJxHEPT2DR5unICJ5TZI99/tZZLKNOvQ11lOpGd -KJ+kvAyccCxbtffVWi7hWPpHODyWTmYnyycpbwEn5OzeZ1+t5RJyeiMc5nQKO0U+ -SfkVcMJj2c6fvlrLJTyWdoRr5Vg6lZ0qn6SsjHDC49iaZ16t5RIeR8+Es4+jz7HP -yScpu4FX4Tj29z1/qOVRHFfMN+PoNHaaPENJ8io8nm145o1aHsXjaVKg1PE0k82U -GxSFV+EJ7Fe/CgZPoGMIknE8gWaxWfJsZRSvQoVtWP1JOY+iQlcSJOOo0Bw2Rz5d -WcEJa9g33/64nEtYQ2tCxDV0BjtDPl35ADhhkn3jnrcmcAmTHYiTNJfNlecpXwFO -OJ794o4/T+ASjqe7IgTJChxPZ7Iz5fnK+givwgnspz99cwKP4gSaS5CM4wQ6i50l -z1emccJa9qdf/3UCl7CW/h0ao5YWsAXy2cofgFehyh585q8TeBRVuipQSaWFbKF8 -tjKPE2psz4b3J3AJNfoGBEGo0TnsHHmRchdwwons3Vven8AlnEgfhMMTaTFbLC9S -/g28CuvYD/70/gQexTo6NmBdR+eyc+Xzgqygs1c2vTOBS6jTi+UBa53OZ+fLS5Q/ -xzjhJHbnve9M4BJOoleQIFmBk+gCdoG8RNmOnHAye2/bOxO4hJPpBgxmT6YL2YXy -EmUj8iqcwl69Zq3KoziFxgaSp9BStlRepozhQ7Ge7Vi3VuVRrKcYj+JUGhGQ1NNF -7CL5YkVKxnEqXcIukZcplZxwKrvtl5siXMJpdF2EIFmB06iRNcqXKpsgoSYw+uCW -46IPbjkuPuKYMfJYfuxx444/QalJjp9Qq2oT6/RJk6fUTx0RwwgrFJAfjdPY/Ws+ -iPAoTqcaLuGJtB0IknGcTgYz5CZFSVbgiZRiKblJ2QaccDrb8rU3J3AJT6JtQJCs -wJMozdKyULYCr8IT2Zon35zAo3gyTSBIxvFkambNsqmM51V4Evv3428P4lE8hY4n -SMbxFGphLbKpjOOEJ7PbVr49iEt4Ku0CgmQFnkoWs+TLlIeBE57C7vzqmxO4hJ+j -u4AgWYGfo+VsuSyUm4ETnso2PvHmBC7habQRCJJxPI0yLCMLpZVX4efYHXe8OYFH -cQadSpCM4wzKsqwslFN4FZ7G3vlaMeSWEiSOTyCsWzUR1q2aGE9Mm37iSSefcurn -Tutpu1F8GM5g/9mxN8IlnEk7gEvYQJtDj8wkm9myo9wDyQpsoBzLyY6yKcKrcCZ7 -7609ER7FWWQTJOM4iy5nl8uOciEnbGD/e93eCJdwFm0GAk44i72wdm+ESziLrgUC -TjibXbNhb4RLOItuAoJEXSI2ZP29Y8vK4mNmzGyYNXvW7Fmz9/+3mIQjvArnsGdf -3hjjUZxN4wIUs8llruwpw3kVns7u3HNTjEdxDi0OBueQz3w5r8zkhGewPRs2x7iE -p9POcImdTq2sVW5THgZOOJd9c9PmGJfwDPohECQr8AxawVbIbcpzwAnnsaev3Rzj -Es4NFExW4FxqZ+1ym7IOeBWeyXas2RzjUZxHlxMk4ziPrmBXyG2KzQnns1d2BpLP -pN+FOedM+jz7vNymvB7hhGexmx/YHOMSzqdnIgTJCpxPV7Ir5TblqQgnXMCu3VjE -/Q0g4FV4Nvvbh5tiPIpn0SWBsLPoC+wLcptyCidcyN7dsjnGJVxA74V6LKAvsi/K -bcqbwAnPYe9fX+T2OBAk9ER5AQur5LKyuDzn9DPmzjtz/lkL5s478+yF53T+7fhl -hIQRVojyKlzE7vz5TTEexbOpJUBwNl3FrpLzylJehYvZ+2uLtriyKIEKK0MJixZ3 -MOvN/py580IJEV6F57I3Opi0F5lEC6uLMM/tQNQHE+RVeB777zP3RnkUF9IoguQQ -XEgFYAWQV4Iyklfh+Wzrq/dEeRTPIYsgOQTPoVXAVoF8NSjLOOES9u91345yCRfR -eggYLKLVwFaDvAaUdk54AfvX4/eM4BIupusnEyQrcTGtBbYW5HWg3KJxwgvZYzsD -inNpixZQnEvXALsG5HWgbNE44VL24n9uinAJz6MXJhMkh+B5dC2wa0G+VPnaZD4M -l7Ff/CpIm+fTE5OD7BmUiCF4Pq0Hth7kS5UvT06MSsRvHPz6SvnGwa+vlOPl552/ -5IILly4bIWGE7R7ECS9iq/fdM4hLuIS2AkGyEpfQdcCuA3kDKJuAE17Mfvz+g4O4 -hBfQ32IEyUq8gK4Hdj3IG0G5O8YJL2EfbXpoEJfwQnq5nCBZiRfSDcBuAHkjKC+V -c8JG9tYfHklyCZfSB+UEyUpcSpuAbQJ5Myh/KeeEl7J9bz+S5BIuo51IkKzEZXQj -sBtB3gzKw5gYlYj/ZPjPCvJPhv+sIMfLL7r4ksZLi3rcDZzQYB++dM8gLuESehQI -EqMTRxQKkWsKcvg3Xm5cfEnjpUuXjYhihBEnbGI37HpgEJfwomBCshIvopuA3QTy -zRAu/RS77o0i6P+WEyRGJOIFeXdBLsi7C3JcakpdunTZCAkjrAC8CtPs4017IzyK -s0gQ8CoU7Nnf74nwKF5M8wiSR+DFtAXYFpAd5XRO2Mz++ea+CJfwEnoylH8J3QLs -FpC3gvIo8GFosh8VfhrhEjbStcAlvJS+FSFIVmIjbQO2DeStoFwDyUq8lG4FdivI -W0H5ZoRXYQtb/UYhzqNoUFMQngbdBuw2kG8HZUliZKL8f4+4e6VcVhYfPCM9WzSb -LUU9dkc4ocV+fuO+OJewiR4L7dJE24FtB/kOULYDJ7yMbVm3L84lTNHNQJCsxBTd -CexOkO8AZQPwKlzOHvvwr0N4FNOkEySHYJruAnYXyHeDUserMMN+t/F3FTyKgkYT -JIegoHuA3QPyvaCM5IRZdsc/flPJJWym20MJzXQfsPtA3gHKrZAYlcDy1x7g5a89 -wONoXbY8k8kuXVZsyaOc0GY7dm+PcAlN+lmUIFmJJt0P7H6QHwDlJ1FO6LDH7vpK -hEvYQn+vIEhWYgs9COxBkB8C5b0KTphja34QUFh0byVBshItehjYwwHFi0M44eXs -1bu/HOESXkavRwOKy2gnsJ0gPwTK3igndNmzD305wiVcTqslgmQlLqdHgD0C8kOg -/DnKCT32xtYnI1zCDK0mgmQlZmgXsF0gPwrKf6s4oc9+sXVfnEvYRE+FeT7P9jy3 -p4JHMUtqYN0sPQbsMZC/BEptQk7gL3fChoJcVhavtJ3c5a7nX5bPZLLF2Ge8ClvZ -2qeKHPRgY1W+Z9Du1XJZWTzWGtoySOmDeRW2sR1rfx/nUbRpLkFyCNr0OLDHQf4y -KDN5Fa5gP3zi6SN4FB06kSA5BB36CrCvgPwEKFpiRCI2deP9cllZfLB1WduK0E8B -73mcsJ1teOq/g7iEOfpS6KYc/Q+w/wH5q6DcE+WEV7A1+7YM4RJeTrtCisvpSWBP -gvw1UHZGEyyBsZ17eWznXh6Ptl/REQQFiRN+nj2/8cMjuIQufWsQQbISXXoK2FMg -Pw3K1wYlju7sM6Ltn+8MnwgfileyX3/v3iiPokeTgioxOtDco68D+3pQJeo44RfY -i6+tq+IS+vTOqQTJSvTpG8C+AfI3QfmknhN+ke3bt66KS5inj+sJkpWYp28B+1ZA -8XF9YmQCR311rzzqq3vlePmV5y/5wheXLgu3i0HivYqtfXxfhEvYSk9GAgVa6dvA -vg3yd0DZFeHVWAD2s98FJG30j0EBSRvtBrY7IHl+EK/GlcA2/nlfhEt4CT0TNlyr -gO3ad9sYHsUVdFSg2Qp6BtgzIH8XlMrEqET5xvK7V8llZfH4VQVYCeYqKHptd5RX -49XA/vXHp0dwCdtpT7g62+l7wL4H8rNBhqjG1cD2/T4guYJuhwDXFfR9YN8PSG6H -xDHdqtLgK89fcjWs7pQyiB+Na4C9dOP2KI/i52k2j+KVxVj/PP0A2A9A3gPKKckh -eCXtBbY3+FrDGa4Fdvvq+6Jcwi/Q+iiX8IvhcVQlfoGeA/YcyCtBuTaarMQv0g+B -/RDklaCsj/BqXAfsgwfui3IJrwqPAyrxKvoRsB+BvBKUb0d4Aq8Btu69e6M8hgWg -V0O1CkA/BvbjgE8jJ7wW2K++X+wtjiHgCVwPbOsvXxnJY7gS6I7BBMkqXAn0E2A/ -AXkfKGuRJ/A6YG/ve2Ukj+EqoDUYEK0C+imwnwZEazAxLoHjtz4uj9/6uBxPrIG1 -sA6ugWvh/CXr4boOyxUu5cNwA7DH/rCns4mP4sV0JgGvxuuB/fvlRyUu4dVAIwmS -lXg10M+A/Qzkn4NyamJ0Iv5EbPcq+YnY7lVyHDdAs3l9J2uJV+NGYM/e86MIl3A1 -0NKAw2qg54E9D/IvQKnk1XgDsF3bO0iGEfBq3ATsLx2l7qtAwKtxM7DVT329nEu4 -BugkgmQlrgF6AdgLIP8SlCSvxhuBffCVr5dzCdcCHUeQrMS1QL8C9iuQfwnKsYkx -XS3qkI1wA2wCczPc2BGqhSivxpuA3XZ/B96mAMzNwG7c+zpxCdcBHU+QrMR1QC8C -exHkl0AZl5AT5c8d91RBLiuLV90EN8ANcANsAvNmKObPHK/GLcA+furtwVzCa4BO -DuBfA/QysJdB/jUoJyeO6WIyJGSyCcwtXSyOxFuAvffuz8bzcrwW6L3rJYIk4bVA -rwB7BeRXQXn3einBEgivPT8FXnt+Sjx6C3Rkh93H8SNxK7D37nl4PC/H9UCP3RIJ -GKwH+g2w3wQMHr0lkjg6EYs8vTlZVhaPbu2anuBH4jZgz75w33hejtcBXf0iECQJ -rwP6LbDfgvwqKCtfhMTRiVhs515eVhaPbuucXpD4cLwV2J8f3RfhMdwAdD1wCVvp -iUgQtxuAXgP2GsjfAeU64NV4G7C/PfVCNZfw+g5nXw/0OrDXQf4dKHpidKJ8e+Wm -lXJZWbziVijASjBv6wjpuyO8Gm8H9tJbBZVLuBGomiBZiRuBfg/s9yD/ARQlMSpR -/lx099UlOev2DgaFci7jdmC//LhrTcTwBqAnINiXFoAgWYk3AL0B7A2QHeUrwKvx -DmDbPni3lku4CShJkKzCTUB/BPZHkP8XlFsHJcYk4t+DZ9bL34Nn1svx+HaYLZrN -OzrFDuVH4p3ANn7z8cG8HDcD/e3lMBNtBvoTsD+B/P9A8RPDE/HtlZtWytsrN62U -49E7O2DfHeFH4l3Adl13b3H+M7siBInhifjTo/YW5KdH7S3I8ehdHfI2AT8S7wZ2 -64/uKtJ/d0ckqO0o79wtyzt3y/Ho3Z0hVAB+JN4D7OUdW4vs3121H/b3dGN/L7DV -v99SVGfPf6A3nHtL6BN4H7Adv/kIeQw3A308vjf5fd3IdwB7/EsfFMnfHt5b2R3d -yO8HtvquDvJ9+yG/vxv5A8Ce/e6/itz/eERv8ge6kT8IbPUD26t5DG8E+l4siLkb -gd4E9ibIfwalgSfwIWAP3be9msfwpk6am4DeAvZWSJMYkcDB698YO3j9G2PjsQfh -oU777x7Eq/FhYH/52rXjuIQ3A3GCZCXeDPQXYH8B+a+gjE6MTMTh9t0bBgV/4tGH -YemyEeUYYYXdGwbxatwJ7Mff2TiOS7ilI6FvAXob2Nsg/w2UoxMsUV5WBoXd22Lx -6M6uydtiPIGPANv61vcH8xjeAvSNsHm4BegdYO+A/C4og3kCdwH7wY8Dmq2dNFuB -3gP2XkiTYIlYWRnuXMPjsUdgV1d4RXkCHwX266dXR3gMtwF9BMEa2gb0d2B/B/l9 -UP4FPIGPAVv9700RHsNbgV4Lm+Rbgf4B7B8gX6o8KfEEfgnYK7fcEeExvA3oN5GA -0W1A/wT2T5D/BcpdEOKI7PqPEo89Co/BlyAM8910xmPPrvvwhe2//mDYiGEIVHi9 -EGGFwluqXLhU+TdM/ACmwYhRGKFCYf13v/2nUaxQ+Mt3v/2nUXKhEFf+AxM/hBEj -cRAVdq9+Nhi9+4Zng1FZ+Qgm/hdGDMUoFXY//t5Hn4xhhcKX3/vokzHKxzBiJEpU -KBQ+/OSTTz75BFjh7o6Pyiegl+mSlzNSQpea2n3h6UOMTMZJNTpNl4mU7+lSysnb -vl5hZDJOqjGkHGLZeU90klSEX72ckRJ6heOpi4SRnmNlhL5Ua3GyQmtxnaxzhVhu -aKaj5ZabWtZJa6aTMWxTdVxT8x0nk2oxLPu01onqRLWu1nTqVL1OnaRmLDu/otbI -pusna56b0hxPa7YyQjUdfbybt30rK7RcznWaVSWZc51mKyNm5q1MWrg1qiuM9FlG -LmfZpp453EC6CddyruM7jY7fIlzVdPQx3UZVW7Qt7IZNbz7cdtkPHNV09JHdbZRz -nWYrI5a4lv9/ACLnOs2q6ehTTctvyTepKSerLc8YeS/neL6WcrI5V3ie1pwxfKEq -yWbD808Xdo1qpNMzM05quX5JT4DnXDB3oeb4IlObcjIZkfIdV2sVdtpxtX7I0JoN -z28UdspJC3eAwGbbqfl1NepsO+WkhX7BYQaWEa0iU6eajn5S/2zVYTzHrVE933HF -HMPz9QsPs73Sojlj+GIApirFlco4ntAn9c/5YUjWqA3BJL3vSeYVVq7XnPMOm/Lm -FVZOM6+wcqrp6ItKoJiu0WzYhpZrdx0v5eREreloppMWGd/IuU6zZtm+cG0jU4z9 -3ilpZt7KpHX74KAOUHyYlFTT0S85JB1miYxvnCGMXDGHuTVqYPniT76je/83+qRF -xjcaW4SRU01HbyiJkU+zjKoki2ibrYxwa9RiOj7YDPNp8rQOkCd+SvSoSnKx8DzL -sWvUdD6bKzHzwQb0frF5oRDVdPQpnxLQpZDyuYxjpGcZvqFPHIAmrvCEr08bgCDf -WC4W20bOa3F8T68fwMzFvuH6anPeTtXpo7tVwXCoYeF5Hc4+uyQV7ddKPZZvWjQb -+Yzf0FFqejFsOoxx3112sacxHf3kTwGtKslULl+M7E6oHT5YchgRdpbcQ4EV+OQg -HazrtSXGOGAnsUC0LXEtX7gDLCPBJJ316Eu6uFk9bXmovWxHsQw7oNJqO7pzqEep -CzGKzwxJt9I3rvty6l3NmoJqdmRTvtly1AWibZEw0sJdbF2xn8R6qMYKhIR/g+a/ -xLUNGSu1/Awn7wkt1VJrOsXC1wlIP//gMun++WquMIr944FAZKzU8hYn74la09Fa -dTVtGRn9wp5O618zWwqiB18t5di2ajr65/qJQ0l2sagJUBUTZ+NngK3TCD3rYB8a -qbNEs5HP+LMsI7PYdw1fmO369IPWrluW6ktsd8MYqcvzlisOUvBCyzZ1q0TfrPBd -K+X03LeIFTnH9YVbYqni9qxrpEVdINoaOsdnpFLC8xYYvtUqGpxMPmsbrp7qj++6 -WHa6pbcwzUilhOc12oGAxlRRgmo6unE49FHtUmWKJWt20Qz6osOkR4eqqunoS0pC -56DdoKZcYfhivmN6nWDPOUxGbzZSvuO2q6ajn2E6qpMTti8yIit8t121Smpul1oN -nWjm5O1UyddOS17cH3Ad2+Z+Ce2SHpT+fs1RlWSw2wl2dZ0W1Bf3PdsTbquVEl07 -KdM1ci2qkuzQf4GTFsXK0+Bkc44tbF9v6U/s9Evj/cvXbCctPNV09LP69tL+p6tK -8nTXyLX0xO199sBD8wUZ+GCAzwwq/Ly+Q7OodLBxcFutlKhRLdvyZ6/whe1Zju3N -sNMLrZzIWLbQlx12jTvEm45e0z+gC0Sbfnbf9nB8kUk5GVVJlvTUnvDzuQbHbrbM -vGv4lmN3hqCnX3zYFCvK7t5w1/etWnFOd7yL8nZ/5y0QbQ1ONmvY6WJLcPj8VISm -pYr8Az915WUv11w3SUs5Ta6hKskiiBpVrBCpvC/0+QNJZvvlWio4WVKXDyB4dii4 -Qa/pN61+ZNawbNXN23NtX7hGyrdahX5qf5CnsumejUHWsIKuDoMPbt7WZ/fHBgfg -FB5Ke6rp6IMDhlnDsvUjiq19+K1X+3eozXrnqbPrpFTT0Y81HTXfJNzgsP8KI6dd -YeRSjitUW7Q1OHnbF66nn9UfLbuS+P74aZ6RzWXC2l97IJkLRNtiI5vLCHeJ5bec -nfMtx/b0OX2vk2KS0bqqs93BJj3fMU3hFteNOTAt+qj4vUSWCDedT0tgvWb3BrzE -NXINjivU5ryd0vsXZQewvxNaUTUdXe7hGzUcC5oVI5fLtOvjenhGVZKhCWvUUo/M -Gpglu8VDxjFN4aqmo582QCP3MpM+ve/Mu19Dzw/k65MGOnWBaNOPcYUpVuQ0r932 -jRXFQ/Xw2NKyPb93z3DIq7VUnlaUpppOX0jcvC30sd1mdkNa5KLXleRkIyNSfouT -NTwtZ7i+lbJyGaG16lpGrBCuel7OdI200C/qT7briMOudN43dy0nxPKeFxkHmqIq -Sc938yl/vlgRnBYLsfxg73wOJEMLJaimo8/vhxaqkjSFLVzDd9wGx/bFCr9GzRmu -J84VbnaBc5aTtpot4XoHewl0QJyma2SzRtD5z+iHufsGqs/uh8cOzGKxuDwv7JTQ -zzgkJLMs77K8nfItxz54QF6+KbD/6a6Tz+kzDwlQyOMQzdueE/qsQzJvg5Hz867o -dmF7oLjouUYWiBW+PtV0HDPIHZ1X+8HNeFO+uWtH12xlRFp4KdUWbYuMtjlWRujp -njmtr8OxAcjQ0sJLNVq25YenUAOYGu59hBv+1y8dUDXqvxW0YEs4IHR+e078H6Dz -23OiC91FqYyTT6tF1VJOVjMdLeOYpmWbmpGzWvWOb7kmtdnKiMbQ2I1FmsZWvfNj -KthCNQah0WjZlq8vH4B1BwCkA2BRopprUk1H1weiShA7ul5VbGnVtDPXtvw6vbJ4 -OF78QZ+QcttzvqOtmDJxupZbbq1QleQCIytq1DlWJjPHdbKLZi3ozF8tPfU91Bre -U3oIwXT0USUjas5wPdEgXN9qtlLGZ3KTUIrDcL2wCesGYmFPEGpJuvFsp605YywX -6SbNdDq/qa4w0g0zGoTrewd7MXoA1pqT8oIL5W6b0gPACKJhkj6m1KhKskG4/kLH -ydSoM9LpBuF+Bj1aqV1TwvUbc46TUU1HH9q9B2twsjkrI/TKsDXr7MN6pa9DDThX -mGJFrtg7qqbTKbEDQSKkUM/Ke34HqtNKPG20eZrR5tV66eW1plPbqnd20p5Xch0q -7HTOsWzfC86UdG8gafjghZWINR391OXTPNVyNCNnZY1Ui2ULt13LLTe1jhygJBen -WkRW1Kgz0ukzbafNPrc9J5ZYfssCIyv0c3uu9j6q26eI0rxUi8gGh11TP4X0AKg8 -fULXRK0547SlHNt3nYzWWtckfKNONUp08PTzeqaqfoHfH2PNFabl+SK8QO6vTYuF -Llhb5zqhnQe0R/gUQUWLNgZ1OMRWU7RsKmMJ2681HW15vkm4tvCF1+GAIBNM1C8c -uGv74NrNQtWuaM6IlK/67TmRsezlXu+7nkNexcWDmmIg1xUzSpdcb2b7Yt+1bPMz -OCQKxWhBOxMmkOAHdU7eTp3drA/r+K4kXb89J2rUs4Tf4qT10cWZPQdmti8wskIf -0cHnfCOTF8VJxbHPIA0GsrRWI5MXwfFHMXaMnKWlHFdorT0X1NyBh0wJs24hUt8l -rHtiMnKWp2WFb2itdeqMdPpcJ9hinC9cz3JsfdlBQTiwiAOB0ox01vI8y7HDpR8e -mh8g0fQKsP4lmj5FdANWel1vOh3PCTTlm7WgE1UXFdPTue05oS/pGe59oCmpavvh -q+VcJydc3wpvkMZ1+Uwz8r7jpYyMZZtaqx5m34NPLAfgqJnCFq7hi3Sx/z2hO4IW -x7Wu6O6YIoaB3AF8Gs9eKE4tbiX6t1dUknOsjKhRLdvyZ4lUxtMXdLO6Y2aEZtqO -51up2qyTFhlPc3LCNnJW66RwP9L5tfHsnLBnLJzbOql0C9KrFe6vw/sUrXXKKhq/ -9EDsU+YGyqoTOzuprLFcnG2LhYbn6UbP8DzkShD2c44tcoYXdD3FzrKjheyQfHSJ -3TNWk5a7PMCpzzm4BROy0MKnslXT0f2Stk3YrU57znVWtGumU1vsU2pzGcMW4VhQ -PLSs4adahKt1eDoYavTbc6KxONTYOqnR8w0/7zWmnLRotOxc3i/1/lU97dk/7weS -BgJQ64WiGBcNJWYdMNdwwU7Vp4UR1ff5i+e7XunlvOk6bfrygejfj4Xr+a6neUHn -4DXmbc9oFo2mU6cHHcbM/nIohTkjlxN2ek4+k1lgZIV+Un94dB41Gd0n9wtA52Ql -OdvOZ2vUvJ01XK/FyCwWIq3P7o8ZSnicJTzPMEVPNgOFEqbB7jwuH1DshGchmtMq -3IxjpHusmnC0sWO0sXVS1+egTIYHNu2f1XoJ5Zei6/xcXClzDlXbYLFM1MeW8Mm5 -Tlb4LSLvBXeWzZ4aHEv16zGf3lcRvbhp2bTnG8EZpBxui7tudxYFibdGXWxlcxmr -uV23epr2MKX2UKLmFeX0NOUswzdmOaaWNnwj7Zi1hilsP3hZK+9bGc1LufmmJuGq -M9LpWeGD0YtELmOkhOv1vpLqX/bsp0it+CC2ajrdnsnr5/QwOU7Ujy8+QZEStpf3 -VMvRfNdIifCv5xt+uKnr/c5aX8qEV4MHZhnKCLmbjn5Mrwmu8Jy8myrKXjiQrrOn -7A5WnR9U09FLT0Fas22GKzTTac06WUtrtbL6FM1vz4kw3NX6usn1B/uc6oF5BzXa -GyiWSROnThoQeL1+8mS9Klxdqi3azsmLvNCVEvWdnLBTju0bli1cTzOd2rRlCs8P -AmRAe6ISWAdiqhkZ03EtvyWrmo5+ZsmMAzyI6IqUsFqF2/V2UecvlzW7wcmtO4Ag -6ZzcBzvNa88GjhndMykVjw3dGjVl5PTjSjJuWHbD9xO04jOkIayLBhK7fTPUMsI0 -Uu2NufC1BtV09AklU7ofHWpGm9fzmPCSg8PSF+OOLBQYrPTl1RWaL1b4YQdr2aZm -GbZh2WmxQs0auXBhDeycqGRHdEDemm80ZUSApaY/9KGLJnZzZCrVrplO7WWeY5ee -t4avR1q25asTB3btVRolfTHveMbAVU2n+4tF+5+WFt0wDeSgoN/MOzCppqOfUKLJ -irRZazrFVjbnilyXV3X9nIMLs148S7w5tgRxL7rQi9XF0zrV8o0mL6gbh/9cq3gw -aDUbYR3pFOqlWkTasi2/6wrM9Sc2ms5ndzppeNnG4BVw1dPXQM/sp6aMVIvQ5gl/ -pmtYtqfNtX2RyVjz5qaFoU/UJ6t1mp/Naac78w07rTU2NtaZTnjc2xgauzHlZBuL -j+k3Or7INHY+etmYyqZ7/pRrdx1dmTi9fmr91NTUSVMm1qXTU6dOSaen1QvRVKc3 -6en0RKHrKSNVl5qqH6vlPVfLWE3aimn1jfWTa8N32k07r2WsppTqOWq9rtSn6yc3 -1U1tbjJSU6dPF/XT0sZU0ZSePn3alHS6eXrTlFTTpKlTRL2u9sEuXZux7PyK2hXT -6mvrJ6ueo+q6MmnKJFHXVJ9qmihEU2ritFTzpObmaXW6MMQ0Y0pd02TRnK6fNlno -sWWtac+5WI8va/XavZSRyVz8/wcAHIyQ3X1cAAANCi0tYjUyMjM4OWE0ZTk0N2Q5 -NjU3M2MzODEzNzdiNmMzNzYzMWMwOGZhYTgyZTA1ZDdmZGQ4MzgyYjg4OTM4DQpD -b250ZW50LURpc3Bvc2l0aW9uOiBmb3JtLWRhdGE7IG5hbWU9InNhbXBsZV90eXBl -X2NvbmZpZyI7IGZpbGVuYW1lPSJzYW1wbGVfdHlwZV9jb25maWcuanNvbiINCkNv -bnRlbnQtVHlwZTogYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtDQoNCnsiYWxsb2Nf -b2JqZWN0cyI6eyJ1bml0cyI6Im9iamVjdHMifSwiYWxsb2Nfc3BhY2UiOnsidW5p -dHMiOiJieXRlcyJ9LCJpbnVzZV9vYmplY3RzIjp7InVuaXRzIjoib2JqZWN0cyIs -ImFnZ3JlZ2F0aW9uIjoiYXZlcmFnZSJ9LCJpbnVzZV9zcGFjZSI6eyJ1bml0cyI6 -ImJ5dGVzIiwiYWdncmVnYXRpb24iOiJhdmVyYWdlIn19DQotLWI1MjIzODlhNGU5 -NDdkOTY1NzNjMzgxMzc3YjZjMzc2MzFjMDhmYWE4MmUwNWQ3ZmRkODM4MmI4ODkz -OC0tDQo= -******************* -HEADERS: -User-Agent: Go-http-client/1.1 -Content-Length: 1699 -Content-Type: multipart/form-data; boundary=b35b2678a24e0d9d56eab56e967e97043d914cbd87bbfa0b007f10fc2e7e -Accept-Encoding: gzip -BODY: -LS1iMzViMjY3OGEyNGUwZDlkNTZlYWI1NmU5NjdlOTcwNDNkOTE0Y2JkODdiYmZh -MGIwMDdmMTBmYzJlN2UNCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsg -bmFtZT0icHJvZmlsZSI7IGZpbGVuYW1lPSJwcm9maWxlLnBwcm9mIg0KQ29udGVu -dC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0KH4sIAAAAAAAE/7yV -X4zjRh3HcRJvJskm+W32/szl9u58vrb4gtZOZned7BsqR+Gq8kcHUqVWxWePJ45Z -xxPZznX3nkzVk6BC4hlEpUrlAcQfUSQEgtJWCITKQ6FC8ADScUItB7QCUVqkdlsd -GsfZ7QGPu81Dxp7v7zef73ztZD768z+/de3xr/7+zePVEpKgUC2hIpQ++ezf/nGD -P5BdX06//aeS2kQSfuEHjxRUUXUHSJ0qkkDGsrKgnVSbqIC/NRMLcFcmFqCMywrS -7lSbqIhffudJ0VmES5lYhAquKFXtggqohH/09JMFVUYl+H4ZpE4NlaCGa8qi9p2y -ehrJ+Po/ny6oMpLhRUmV0QLckFUZleHtAohiGeq4rjS0X4ibBWjipgLadblTQ2VY -wksKaH8vqE20gF/65h+EBQSXMwsIWrilLGuWCqiMH939o7BQgWelzEIFjuAjylHt -x5IKCOGvf+VmUZVRFdJGJlfhGD6mHNdu1VVAFfz6i6+L7ho8ImdyDTDGygltt6QC -quKfvJF1L8K15UxehDZuK8e111qtZZBAxFutSoViSV4oo0pVBVTDL6SPiSXr8Nta -1lOHk/iksqJ9r6YCWsS//vcXhNyAH1YzuQGn8CllRftLRQVUx2/ezOQm/AplchNO -49PKivZFpAJq4K+99XnRDfC7WTfAGXxGUbTnBbuJ//rlx4W8BO8Us+4lOIvPKqp2 -vagCAvzUv7LuFjw/627BOXxOUbRXKq3mfDsLtcV6owkqoCX8y8/lW3mpBtK7Spbm -JS386huZ3wZ8owoi8WX8m90MAvByFaRWY76u3KovN6F9FEmQ3kgLOE1f0ZX0snZH -986B1G6jAqTpYz995uYKTtNXxaikaVW7q/v+9glUhPS5R3/2jNCe+JIYlVTRtO75 -9hFUgvS577729q0zOE2fEqPWabeRDGma7t4SHwmnT8wvtQ+Q95FybI8nAYuJTPk0 -TEiRTqakFtohjxnloRuTs9E0TPwxM/wwYVFoB0a8E1M7CPRP7cTUDgKTXDVGfMyM -UcTH/Crbsg2PG5Mtzxhz1/B4YIeeziPPSDgP6Mj2ww9e6epdvbfq8Z5OevqaHvjh -dHvVHrvmuhFH1IimYeL/H6Rhx2MrEMVWVqzHZHnu5pL98J6hHWP0HhnK6TNTusdJ -M5/RczNkdNBecsB83EfXc0V/OPITRvhhga/mnBk5fxIe3+ffn/G9w+LP8dPQzyI/ -5c/fzAkPAt33Qh75offhix//9KWLnzj4B3Abzhi6e0ZO3KboWueeC+fzMFohS3St -E7Jkf8466IBClgg7Ex7PglmaQSkPw7mPBw6DKTAeJ4rgj5Jkok9YFPtx8iEehtm7 -EOUpsMOgC6KRRHYYT3iUCCNHnenQ57rWmcHP6/cE03hEPnPQ9Awz+xbYs3v71zrv -SuD87Pd4H+cT0sr/2XQvYjvc+SyjCXEO2lbOMMYeHdvRlrC2x42pHebcZl6ne/RC -ZPshWdmfuNv7mB1t3c+jLRbpw2lIycGnl9OETWFxOb/X4504YeM4sekWoYeVjThI -5kfIsTnZu23f5Jr033Sd2nTEjHtZcrdILDYuhgkLAv/eiy6zSZes6z0jGU+Mj/D7 -7NA1LMvqedxypn7gWp6fjKaORfnYGrMk8im3eMICi/IgYDThkUXHrsUTFliUBwGj -CY+syU7EidbdNPtmn/bXNro91+33N1x3YDLm9IhDXLfLCKE27dE+OWdM48gIfMfY -HpiWub4a+OF0e9ULp2KS6jHXTaKZrrnu9PpDx6b9zU1mDly7zxx3c3Ow4brDTWeD -Omv9DWYS3ZjGkRH4jvG/y7mr2Tm8uj0wV811sTIh2trGGus5JnW6jDm0O6DDteFw -0CPMZgN7o+ess6FrDtYZWXjwihvzh0j1wSvxTkztIHjoPwEAAP//3t3OysELAAAN -Ci0tYjM1YjI2NzhhMjRlMGQ5ZDU2ZWFiNTZlOTY3ZTk3MDQzZDkxNGNiZDg3YmJm -YTBiMDA3ZjEwZmMyZTdlLS0NCg== -******************* -HEADERS: -User-Agent: Go-http-client/1.1 -Content-Length: 7007 -Content-Type: multipart/form-data; boundary=cd0010cac6db5c74e2d5ff5b92ad4b2d64fd9d7e41555d85cb80a5ab0cb7 -Accept-Encoding: gzip -BODY: -LS1jZDAwMTBjYWM2ZGI1Yzc0ZTJkNWZmNWI5MmFkNGIyZDY0ZmQ5ZDdlNDE1NTVk -ODVjYjgwYTVhYjBjYjcNCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsg -bmFtZT0icHJvZmlsZSI7IGZpbGVuYW1lPSJwcm9maWxlLnBwcm9mIg0KQ29udGVu -dC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0KH4sIAAAJbogE/7x6 -e3gcxZWvajQtHY8s63jAdlm2cbuwodWWuqWWLMk8jG35gY0tG9vYgA3jnp5Sq/FM -19Ddowf7yEBszMOAN4TYgAmPEEgIEHazG5Iv2cQkJLDf5jqbuzeEVza7CXvzERIW -uNkbSAjc7+seSWPJ2JIf9x+pp+s8fudXp06dru6r40AwtrNYlBNxqMR4Ig4SxhJx -qAqvqzHGpgOh/3vPDytZHAguZRLE8J/iSNQEEAQK8iTlErUGYpigCblG+UaczYAY -/fNvXqxkElTi/RKTII7fJUjUGqjEyXSyXKPcK6k1EMdaWitPUW4nDKGSHvi3eyUm -gYQ3V4ayEtbROhmV12MMIU53/9NBiUlQhc9WIVFroAqn0qlyUvliVZImIb73G/Pj -e78xPxEnscp4fRXEaPEwMASJHnj+oMQkqMZ7q5CoNVCNZ9Gz5KTy6SpWB1X0nW8e -klgcAGeGMQGeTc+WpykzGUI1vfuJxyUmwST8CUGi1sAknE6nyzOUFwirA6D3H3pc -YnFIoI9ETUACKaXyDGUTQ5hEf/rzULcGvxvp1uBMOlOuV/6RJGcmgRy8aS05eNPa -hCRVVcOk+iqI0WJxKZsBCfrGEHn3jZCXnJEE8vLfd5KX/74zUZkYjlFh06GG3nrL -7iYWh8m4mElQi/fHkKgJmIyz6Cx5tqKrNVCLc+gc+Rzlvhirg8n0xUf/rYnFYQpu -RqImYArOpXNlWTmfIdTSr/zg9SYmQR2+GkVdh/PoPJkprxCGMIXe9OLrTUwCxP8m -SNQaQDyXnisz5S3CEOroI8+/3sQkmIpvECRqDUzF+XS+zJSXCENA+sSPX29iEiTx -0RgStQaSuIAukJlyY4whTKV7nnu9iUlwFj4XaZ+F59HzZKYcJqwOkvT/vPCrJhaH -s7E1xH02nk/PlxVFZXVwFt333BtNLA7TSoPTsIE2yKqisDo4m770Ujg4Hc9BoiZg -Oi6kC+VGZTarg2l03+6Pq1kcZuBfIlETMAObaJOsKQMMYTr99tsfVTMJKO6JIVFr -gKJOdVlTPiAMYQb91sNvNTIJZuJzBIlaAzOxmTbLLcrfEYZA6b8+8JtGJkE9PhhD -otZAPRrUkFuV22KsDmbSH//4zUYWh1m4BomagFnYRtvkVqWTIdTTX7/6u0YmwWx8 -P2J6Ni6ii+R25VeE1cEs+vhzv2tkcZiDnwpDmoMdtENuV9YyhNn0hX2/b2QSnIPf -IiHqc7CTdsqLlQcJQ5hD3/3c7xuZBHPxg2h4Ll5AL5AXK+8TVgfn0B/++veNLA4y -nhualvFCeqF8Ubgq5tLX9r/TyCSYhz+rDk3Pw4vpxfIS5TdVDEGmn3/knUYmAcPX -AIlaAwwvoZfIS5RDwBDm0fcOvtPIJDgX74JQ+1xcSpfKS5Q7gdUBo6/fcrPG4jAf -54We5+Myukxersxl0+Bc+ujemzUWhwVYxeJwHtaHIguwi3bJKxRJTcB5uJKulJcr -UxjCfHrfT/fHmATn4+0xJGoNnI+r6Cp5tbKfJBuTVRUV8cfvmZ+or5lcO6UOpybP -Onva9Bl0Zv2s2XPOmSvPY+fOL61KYNNhAf3ing9iLA4KNjAJGvAQQaImQMFL6aXy -GkVRa6AB19K18hrlIGEI59F7nn2zkUmg4sGIYhUvo5fJ65QDhNXB+XTP199sZHFY -iI1I1AQsxPV0vdytLGR1oND3n3m7ksWhEc8LBxtxA90gdysLGEIDve/GtyuZBE34 -JAnDasKNdKN8ufJlwhBU+vl/eLORSaDhg9GwhpvoJnmd8lnCEBbSO7/2ZiOTQMc7 -owh03Ew3y+uUPlYHjfSBB8J8bMYloeNm3EK3yOuUi1kdNNF3ni0l3NVIkvOTVRUV -ZO9NzYnkgvPOVxrUhY1No5mbzepAo39+7pE4i0MLzkaiJqAFr6BXyFuVWawOdHrg -9YfjLA4GOuGggdvoNvlKZTtDaKbv7/1OnEnQirdFWFvxKnqVfLUyyBBa6B+eebie -SdCGd7SFPLThdrpd3qF8TmcIBn36iYfrmQSL8B4diVoDi/Aaeo28Q7lHZwit9Gd/ -vDvGJGjH/xVpt+O19Fp5tfJsG5sBbfRfX9ofYxJ04NfamATnD62TDkzRlLxa+du2 -5MxkdUXFZyb9+41yolrTm1uM1rZ6CWL0cCVDWER3H3m4kknQiQcizU7cSXfKprKf -MIR2+qPfP17JJFiM/xXtZ4sxTdOypTxUxRA66If7v1TJJLgAX4nW2AWYoRnZUl6u -Zgid9K1ffUVlElyIH0TDFyKnXO5RflvNEBbTI29/RWUSXIRPREvwIrSpLfcoX4YI -9f+Y+S9FOVG9qL2jc3EJ9UOEIVxA//RyCfVTBMONqqKiWIzdUpQT1ReUpOMQo8gQ -LqR3PflYJZPgYnyKIFFr4GLspb2yEyXiRfT2N0og/1yNJDkjWV1RUZQPF+WEdOFF -i0tkFQlDuJj+5DNHEkyCJfh0ZGkJXkevk3cphwhDWELv2XskwSS4BD8bpfQlmKVZ -eZeyj7A6uIQ+/aff1bI4LEUDiZqApZijOdlVWlgdLKX/ced/1LA4LMM54eAyFFTI -eWUWQ1hGH/jvn09hEizH+yPDy/F6er3sKfeSZH2yqqKi+hePsQRcvOSSpUuXtbZF -9eBwnCEsp/u+8edKJkEXfjWORK2BLvSpLwfKw3GG0EX3HLmnlkmwAp+MhldggRbk -PuWJeHJ6sqqiouqJF1kivrxryGpRYtNgBX31+4/EWRxWYiuLQ0sJ80rsp/3yVqWF -IaykP/vF3jomwSp8ZwkStQZW4QAdkAeVj9sZwip65MjeOibBavyoHYlaA6vxBnqD -PKh81J6cmayqqJj9Dy/KieoVevPKVa1tUYXbTxjCavqH//xmPZPgUnyBhIYvxb+g -fyH/ZTQJl9IjvwyH1wxxtQb/iv6V/JfK/SRKqtJSWKE3r760NLuHK9k0WENf/syh -OIvDWlzJ4nAZakjUBKzFv6Z/LX9KuVidDJdhkdAikT+lNLAZsJbev/sLcSbBOrwt -ziRYj/fGkKi1sA5vJPRGIm9Vbo2rtbAebyL0JiJvVW6LMYTL6AePhWrdeHMlErUW -uvHThH6ayFuV78QYwjq6971H4kyCDfg6QaJOhg24m9DdRN6qpFgdrKcv/aBUqc5B -whC66YGfvjaLSbARH5iERJ0CG3EPoXuIfDNRbgaGsIG+feS1WUyCy3EPhBKX415C -94YSeyA5O1lVUbHwwDNyombN2svWrdebuzcMZf5ONh020qd/9UKMSbAJHyUsDpvx -MiTqFNiEtxB6C5FvJcrDRK2FzXgbobeFv1czhMvp/33zSIxJsAW/TpCoU2AL3k7o -7UTeR5SnCJsBm+g/F38cYxJcgbcSJsFW/McYEnUKXIF3EHoHkfcR5RaiToGteCeh -d4aK346xOthM33/lKYnFYRvOQqLWwja8i9C7iLyfKEuSNFldUfG1qsM3yYmqjZdv -2jwUi8Smwhb63rv/spBVwZX43h0SErUOrsS/IfRviPwZorx7h5SclqyqqCC/+J+L -EpVbhnLv8Hx2DlxBf/rRCA0SXIVfI0yCq7EYRXcV3k3o3WH4f0fUWrgaP0voZ4l8 -K1GuYghb6S37XowxCa7GuwkSVgfb6PO/fCHG4rAZ14Y3rqQHP3i3icVhO6ohD9vx -HkLvIfLniHJvZbI+WV1R8X3y3G1yAq7Yuu3yTVcOzdI0NhWuond++5lJrAp24H+9 -EgtTawceIPQAkQ8SJUhOS1ZXVByasv9GOVF5VWtbvQQx+lCMTYWr6ZO3PzKJVcEO -fO7JGJJI8puzXyzKicqrS5L7CZsK2+m9//xg5OJ7j8aQRFzJTxyWE5Xbh7gqEjYV -dtBXHj0QSb57UyQ5YnNHyXto8xq6+5f3RLBf+CMZ5f2aEUmEa+mjP/8QmAQ78KOF -owSvLRdM0We++gEwCXbg2zNHxZMqF9xJdz9YEjwyWnBnuaBJn//eHyLB/5w8StAs -F0zT3Y8dmsokuAa/X4VErYVr8F5C7yXyfUTpYggW/dIXQoFrhwSuxfsJvT8SiKru -pNvemJeIp60hRg9XsjrI0N8+e+sCFocUMiRqLaTwEKGHiPwAUeZE+xa5//C+ykRl -prWtvjrU21fJ6oDTH333zgUsDjtLa2Unfp7QzxP5QaJMLykWDx+sSlTyYcWDVQyh -hx546weTmAQmfqsyjMXEhwh9iMgPE2USQ7DpD38UCqSHBNL4CKGPRAJRLPDEHpaI -99hDsRTjDKGXvvrN3TEmgYUfEiTqFLDwC4R+gciPEuUPhCE4dPf7+2NMggz+QkKi -1kIGv0joF4m8Wvm6xBCuo6997oEYk4Djz2OhCY6PEfoYkR8nyoOlzIw9+UclUdnr -XFfiES99/ycPvrnvgVc/mFE/AwgW/70Yo8XiW5pc3Kl8iTR/mXSS+tkQw2Lxtu99 -59ezabH42+9959ez5WIxoTxBmr9C6mdBJRYP734+HH3orufDUVl5kjQ/ReqnQRyL -h59578OP59Ji8W/f+/DjucrTpH4WSFgsFv/08ccff/wxocWHhi6VrxKjwpD8vGlx -Q0oPBtw3as1sVlgpkb6OW4FvSJYouIFRY2azwkpFkrWOW/D5sEhN9NPPmxY35lgi -l/e47+s9WTPgmsv7V/DwcpXpB0ZO7xU5rvd6Iidu4LtM3RZ6fpet50RGt0XWdG1N -eLYeCJG1ek3HXdrXrDVrLU22aNGMFq1VyzpuYaDJzGXa23Tfs/Sj3ekZ3pM1A95j -+oFmC2PeKDSKOnRDeA2a4zqB4fz/QaTZwqCj0HTz/m2eE3CvjDX7BievKWp0v0Hb -5jkBN/gZw2jf4OR1+wYnr9nCWOAV3MDJcT2f90SPpqh5T/Q4Wb684GQz3GvQ0gUn -mzF6TvccHuVWz3siEJotjFlH3ddKYCJmzjiIvCd6NFsYc44Cp20OTC/o2njFxogZ -Y4PtBL2FtGaJnG57Zo/pmnp+0BO+JfK8yRa64wbcc81syWKG95iFbNAlslluBcIb -YzA9mtzLr1yzURcBzzZZQ0p6H3czwtMn5ruEwBbGRScArSmqlS+UQhyG2qB53OeB -se00IhwO6VRghXNitJ9gJjRF3cx93xFuSUPrKbiWYVxxGgn3IweaLYyz0oUeR2jd -vH8TNzPc2+zcwI1rR/s61eqXLvQ4Ivqr2cJYWMZBV9axdl0qCj7Xrd4mW5TW1TAg -Y+vJBX5su7rHzQz3jgci61i7ekXB50220PsMLeOYWeOqUwcxyq5uCdfVbGFcUpbl -5aBHyWuKOnKrIUQVZkaLkToD2IY9abYwLhgnwhVR0VjhmNnNgWcG3B40Fo9Td2x0 -R62z8RNjWtcXHI+fpOONjmsbTplqjgeeY4nRlY0P5IUXcE8fmZNS3R8Z6dW6eX/X -MJPLLIv7frcZOH28S2QLOdf0DGt0n3GsMjpicjg3xzrTTcvivp9yQwcpq+RBs4Vh -ls3fScejueXBlPaVlSUajE3jWaTjiGNIRLOFse20wLY8bgZ8nbD9YbCXnybSe0wr -EN6gZgvjUltoIs/dgGd5jgfeoOaU7RojYXUNo1lVcK2yn8PgrhkPuKGNdVxOR7yH -m9e4dDRFHe6mRjAbm4+v7XOvz7H4SDNhe2a+V1NUXsqTbpHhpfasS+TywuVuYPSe -toiP7V93RYb7mi2M9cefpWOra4q62jPzvaW2chi3f+aBR/SFFfhkgC8vONmMsfb4 -qVkKOmw7vD7H4tHzxsqBgLu+I1x/mZvZ6OR51nG5sf20T9WQe1sYDeMD2s37jQ3H -50MEPGuJrKaoZV2hz4NCvku4PY5d8MzAEW7ZVJ6+VVfyfXTL2H780Eo6R+PdVHDH -q9fN+7tELme6mVJLcPrmqQRNt0r2w3ka2U78fE9Lq26JtGdqiloC0aDxAW4VAm6s -m8jSPqbVcsfqiR2vjBx3GQ3jljXOypmOq3kFd40bcM+0AqePG0vGg9zKZUY3BjnT -Cbs6CC+8gmusHA8Hn2ApJYJe7vmaLYxJocGc6bjGZK/gBk6OaznTcce2f6farJes -63lPWJotjHNtoRXS3NOEZ+s3mHn9BjNvCY+HHYEouAH3fGP9xGrhsezpvpnLZ6O9 -v+mTfHbz/s1mLp/l3jYn6N2QDxzh+saq46+TUpHRR3Znd8hMZp2wbe6V1o09nrka -1+Y7xmWZc1ucqICN0R4LeJtn5ruEx0uPh+PKshHkR/EvIhY1WxjyqDEtGgubFTOf -zw4aC0bNjKaoEYUNWvmMrJgYk0fhyQrb5p5mC2Pp8evmiWkyFp+EhXWhf6N1os67 -eb/RYQthZ7lWdkgYHhSlCz0jbVGPk+UZ7luay/s3mf2rnCw3MhMibPw+9Az3rZTj -OkH0KDcReGEDwb3ov7FzPIVsOMUmgDBdcLLhzjJudMFgnmul477o/xlBFwzm+Qi6 -HVZWFDJaKbTwJE3oWWHbjmvrZt7pM4Z+5dNaj5PlqYjsVEkm1WcMX1phH5IKUyMV -nu/umsCCmQAQfch55FHLpzVbGMZEQglzxzDqSvuClhFrXCdoMaaUjmFLN4xGyxvM -B0IfWNS8WM/vcgY0Re02c7xBW+Vks6s8kdu0onszv77AXYuPbZ5Pddsa7T2CYAtj -dtmIljc9n3dxL3B6HMs8I2fW5ThMz48q2VEgNo4GoZUdz/qu6O/Jmrt4Jq3bYviX -5nEz07Wsi3uBf7KHgZ9gWheWnx/T2X0CjDAbWo255aQqahf3go1CZBu0ZZlMF/eC -Mzu/FveCVF6IrGYLY8muTl9zhG7mnZxp9Tou9wbDlzVD+amom61enuMN2rJM5jJX -9LtbBvN8mxP0dps5bmwZnYnHOoMp1bUTuNJ9q5fnwqeZjhOIfgIq32gcUdR7sqLf -Em7giaze15LmgdmimWUx+MYVo2keF/hjGdY9bjt+wKMz7hMEOox++J3Lskxmi4h4 -NnaMp+WdEKOpcI+IsDWUmLWyDneDJlvouwpp7rk84P7QBIRZ2mxcNXEgx7F6FEPl -Z9i2GDq3Thd6SgfYm0psbhnM85N+FzHWrp73RJ57gROdaCwocWHmHd0sBMK3zKzj -2nqfESXLyfPwCRZ1m7vcMwOeKW0lS0r7Ydlb0XBXO3bDo6irnCxv0BzXCVZwK+sb -3WWlLzKl267wA8dqyokMz/q6yHPXzDt9rVqPk+Wp4Z+pDXnuLtu4pq+1fB8dU8+P -syDG71of9lUKu2X8umGwWrMRlKlwt08M5j0xMKjboqm0xJvyWdPl0Zge9h45M7B6 -uacPxR4OpYLBPE+VhlJ9rSk/MIOCn7JEhqccN18Iyvn41MmlXuhpIgD1MShKTHWd -UthhAncYnVFqHD/L/MDzyw8ubU/0G7smEv84UtkPPF/3A89xbT9VcH2zh6ds0WK0 -aLYwlo/XQjnMZfk8dzOrCtlst5njxoXjsTH8BGEerbx8QsqKutIt5Bq0gpszPb/X -zG7mPGOsHE8QwwAUdT33fdPmo81MFEpUGI6Gcv2EcidqcXXRx72sMDOjVk00mhoa -TfW1jlyHFSvqwwfP1HqJ/JejG74urZRVpxptuFiajXM8bvOBvO4PuoE5UPqYxMny -8FMS/wy0Zkf5Cz91ccJlZEw7GkmXyOWdLDemRApaSXLss9upPgt43OYD+dI/zRbD -HocQJEtD6wt+MHRvXhn9eU/keNDLC354DNbja+FD2rjeHJUam+NZ03MZPzDDJ/Ly -c8++XL/pcd0WfTmRc/Q+J2cs0oPBPI/ca+0tbe0n+yr6k22HW40ffUQS8XaMxLHM -vDHvOMOlmZ1fFnVUiqLPOfTSC8twL5xQf3gCg3qW26Y1mMpHLyQ1WxiNZSfJZr+v -m/1+k5/Z1WQL3ez3de5m8sJxg4jTsd86jK9fOJ5hvfT1TMipXrZlDegBHwh07loi -47i27piu6bgZPqDlzHw0xRPrWcdjWw/MdJaHWBrGIx9NUfNRvFvWoG6Lput84Y58 -KRSGwT3NcZ1Aax67iMdH5PGNDxWT8N3lODBl+FGYUidXzD8BU8n4ECbNFsb5ZTQN -ZOwmW5R6g7zH8yOzahiXnxySMTbLZrO8Xo2Ri2Zx6tAjsBOYad9xnWDsKd4p19ro -ZYTu9JgW12wx4tS3ennGcZ1g5KjIC5pTthj72cPpAmH6uVT40aXmG3vIaC+aZVq9 -XF/Lg+We6bi+vsYNeDbrrF2T4abRbLRpLXqQy+urxTrTzeipVKrFFtGjZypaDylL -5FKlb0JSIuDZ1PB7vpSVy4y+lR/0hKE0L27vaO+wOloXNbdkMh0dizKZznbO0y1G -2shkmrlhWKbVYnUY5+oF39OzTlof6GxPtbc1RV+R2m5BzzppS/OF1m4o7Zn2tnRL -R0/atDoWL+btnRmzg6czixd3LspkehanF1np1o5FvN3QjmMu05R13MJA00Bne1N7 -m+YLzTCU1kWtvCXdbqWbOU9bzZ1WT2tPT2eLwU3eaS5qSbfxnkx7Zxs3qrb3ZXxx -jZHY3ucP+paZzV7z/wYAGI/sTX08AAANCi0tY2QwMDEwY2FjNmRiNWM3NGUyZDVm -ZjViOTJhZDRiMmQ2NGZkOWQ3ZTQxNTU1ZDg1Y2I4MGE1YWIwY2I3DQpDb250ZW50 -LURpc3Bvc2l0aW9uOiBmb3JtLWRhdGE7IG5hbWU9InNhbXBsZV90eXBlX2NvbmZp -ZyI7IGZpbGVuYW1lPSJzYW1wbGVfdHlwZV9jb25maWcuanNvbiINCkNvbnRlbnQt -VHlwZTogYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtDQoNCnsiYWxsb2Nfb2JqZWN0 -cyI6eyJ1bml0cyI6Im9iamVjdHMifSwiYWxsb2Nfc3BhY2UiOnsidW5pdHMiOiJi -eXRlcyJ9LCJpbnVzZV9vYmplY3RzIjp7InVuaXRzIjoib2JqZWN0cyIsImFnZ3Jl -Z2F0aW9uIjoiYXZlcmFnZSJ9LCJpbnVzZV9zcGFjZSI6eyJ1bml0cyI6ImJ5dGVz -IiwiYWdncmVnYXRpb24iOiJhdmVyYWdlIn19DQotLWNkMDAxMGNhYzZkYjVjNzRl -MmQ1ZmY1YjkyYWQ0YjJkNjRmZDlkN2U0MTU1NWQ4NWNiODBhNWFiMGNiNy0tDQo= -******************* -HEADERS: -User-Agent: Go-http-client/1.1 -Content-Length: 7688 -Content-Type: multipart/form-data; boundary=da789a8b069ffe50cb69dd81e83455e6b8760e653cb5024638996525d426 -Accept-Encoding: gzip -BODY: -LS1kYTc4OWE4YjA2OWZmZTUwY2I2OWRkODFlODM0NTVlNmI4NzYwZTY1M2NiNTAy -NDYzODk5NjUyNWQ0MjYNCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsg -bmFtZT0icHJvZmlsZSI7IGZpbGVuYW1lPSJwcm9maWxlLnBwcm9mIg0KQ29udGVu -dC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0KH4sIAAAJbogE/7x6 -e3gcxZXv1GhaOh5Z1vGA7bJsTFPY0GpL3VJLlmzAxrb8wMYYYxuMeY1bPaVWo56u -obtHD252MxCbd4gvxOFpwEBwwiskbCDZvExCAvfbrLN78ySBsHl9+dgkLBA2QELg -fl/3SBrLDyTbe/+RZqZOnfM7v3OqzqnqviQFBJPbSiU5nYIqTKVTIGEynYLq6HMN -Jtl0IHTHbbsllgKC05gESfznaiRqGggCBXmSklFrIYlpmpZrlc9VsxmQpL/71kMS -k6AKd9UwCVL4K4JErYUqnEwny7XK7TVqLaSwjtbJU5THCEOoos+/+5DEJJBwTywr -YT2tl1G5jzCEFP3j1z4rMQmq8V9TSNRaqMapdKqMyrUphiDRh2/4rMQkqMGbq5Co -tVCDGZqRUdmfZPVQTf/+o4cklgLAbUjUNACeQE+QT1Qoq4ca+sErj0gsBZPwAiRq -GibhNDpNnq50MgSgv3x7r8QkSOMXk5HiNM6gM2Sq7EkyhEl0+5t7JSZBLe6Oh2tx -Jp0pU+V9whDS9Pbf7JWYBJPxHYJErYXJ2EAbZKr8mTCEWvrMz/ZKTII6vJsgUWuh -DmfRWTJVPk0YwmT65Kt7JSbBFPwKQaLWwhScTWfLVHmGZOZkqlPXf2VuIpGeTJJV -Kam6Bialayc3VEOSlvYBq4c6+s6TuyWWgnrcjERNQz2eRE+SJykbWD1Mobt+9IDE -UoDYhkRNA+IcOkc+WckwhHr6f67bLTEJpuJfpAjXVJSpLNcqv5QYAtLbH9wtMQky -+LdqJGotZPAUeopcq/ylms2AqfSt7z0kMQlOwFdrmAQn4l1JJGotnICMMrlW+WWN -Wgsn4qn0VHmKckOSIWToPX9+WGISTMNvxyxPw7l0rozK40lWDyfQp39YDp+DJHNy -pjr59e3zE4l0Xd2UepyaOaHS99KJDOFEesfzd0pMgul4VzUStRam4zw6Tz5N+UQ1 -q4dp9I2v3iuxFMzAmUjUNMzA0+npsqLMZAjT6e2P7pWYBBT/nSBRa4FiI22UVeUF -wuphBr3n3r0SS8FMDKJVMBPn0/myqmxkCJT++JUoHxrwW3HAG7CJNslU+QbJzMhU -JxLkzmvXpqUTp02fQRuqIUlLpaVsOsykN96wvZmlYBYuYhLMxnuSke5Z2EybZU3R -1VqYjTrV5Rbl7iSrhwb64sO/bGYpOAk3IVHTcBK20lbZUE5nCLPoY999uZlJMAd/ -TpCotTAH22ib3K68RBjCbHrtiy83MwlOxv8mSNRaOBkX0AVyu/IHwhBOog8+H82W -8bck4k7GDtohtys/IQxhDn30By83MwlOwYeTSNRaOAU7aafcrlyTZAgn0x3PvdzM -JGD4HEGi1gLDhXSh3K7sI6weZPrnF37TzFJwKrYhUdNwKi6ii+QzFJXVwyn0lud+ -28xSMLc8OBfPpGfKZykKqwdGf/KTaHAezok8noeL6WJ5iTKb1cOp9JbtH9awFJyG -H0OipuE0PJueLS9VBhnCXPr11z+oYRKcjjtixKfjMrpMXqq8RxjCPPq1PX9oYhIo -+FzMloLL6XK5S/kSYQin0R/u/s8mJkEj3p9EotZCI66gK+SVyk1JVg+n0x/84LUm -lgIV1yBR06DiKrpKXqksZAgK/f3P/9TEJJiP78aq5+Nqulo+R/kNYfXQSPc+96cm -loIm/HiEugnX0DXyOcpahqDSF255u4lJ0Ixfi4lsxrV0rXyucj9hCPPpm595u4lJ -oOF78bCG6+g6+VzlXcLqoYl+7/dvN7EU6HhqpFrH8+h58nplJkNopr/Y+UYTk6AF -f1oTqW7B8+n58gblP6sZgkbve/CNJiZBK/4CkKi10IoX0AvkDcq9wBB0+tadbzQx -CQz8FESzDdxIN8oblFuB1UMLffmG6zSWgjY8JbLchpvoJnmzcjKbBq304euv01gK -2rGapWABNkQi7XghvVC+SJHUNCzALXSLvFmZwhAMevePdyaZBB14cxKJWgsdeDG9 -WN6q7CSZpkx1IpHau2tuumFmw6zZJ805WT6FnTp33mmnK43q/KZmTW9pNcprDNh0 -aKOf3fFekqWgExuZBAvxXoJETUMnXkIvkS9VFLUWFuJl9DL5UuVOwhDa6a5nX2ti -EizCO2OKF+Hl9HL5CuUOwuphAd3xzGtNLAVnYBMSNQ1nYJZm5W3KfFYPHfTdp16v -Yik4E0+LBs9Ek5ryNmUeQ+ikd1/zehWT4Cx8nERunYXdtFu2lM8ThrCQ3vfl15qY -BIvx/nh4MeZoTr5C+TRhCIvorU+/1sQkWIK3xh4sQU65fIXSz+rhDLp792tNLAVn -45LI8NnYQ3vkK5TFrB7OpG88W064S5Bk5maqEwly/bUt6Uxb+4KOzoWLzjhzLHOz -WT2cRf/+3IMploKlOBuJmoalaFNb7lVmsXpYTO94eU+KpWAZOtHgMnSoI1+pXMoQ -ltB3r/9mikmwHG+KsS7HPtonu8oQQzibvvPUngYmQRd+sj3ioQvzNC97ymd0hrCU -PvnongYmwQrcpSNRa2EFCipkT9mlM4Rl9Kd/vT3JJFiJP4pnr8QCLchblWfb2QxY -Tn/4k51JJsEqfLqdSdAxvE5W4VX0Knmr8sX2zMxMTSJx26T/uEZO15y1eMnZS5ct -b5AgSfdVMYQuun3/niomwWq8I565Gn3qy4GykzCEFfT7b++tYhKcg/8VV8FzMKSh -XFQeqGYIK+n7Oz9XxSRYgy/Fa2wN9tN+uaj8rIYhrKJ/+M1jKpNgLb4XD6/FATog -Dyp/rGEIq+n+1x9TmQTn4qPxEjwXh+iQPKh8HmLU/zrz30pyuqZrxcpVq8uoHyAM -4Rz6t5+VUT9BkGRmZiCRKJWSN5TkdM05ZekUJCkyhDX0U48/UsUkWIdPECRqLazD -q+nV8v+KE3Etvfm3ZZB/r0GSmZGpSSRK8r6SnJbWrF1dJqtEGMK59N9v259mEpyH -T8aazsOP0Y/J/6DcSxjCOrrr+v1pJsF6/HSc0uvxH+k/yv+g3EJYPZxHn/zbn+pY -Cs5HA4laC+fjx+nH5RJRWlk9rKe/uvVXtSwFG/AkJGodbMBrCL2GyNcSZRZDOJ/u -/u9XpjAJLsB7CBJ1ClyAnyD0E0TeTpS7SKYhU51I1Lz6CEvDuevOW7/+/GXLG6oh -SUv7UgxhA73lK3+vYhJsxC+kkKhTYCPuIHQHka8jyp4UQ7iA7ti/q45JsAkfTyFR -p8AmvJ7Q64l8A1EeTWWmZ6oTiepHX2Tp1IYLhnWXJDYNNtKff+fBFEvBZmxjKVga -4Z8Mm/FGQm8kcq/SyhA20Z++en09k+BCfGMJEnUKXIg3EXoTkW8myocdDGEz3b// -+nomwUX4QQcSdQpchLcQeguRbybKBx2ZmZnqRGL2l1+U0zUbFy/ZtHnZ8ri12EkY -woX0nd99tYFJsAVfIBH6LfhJQj9J5FtJHJyL6P5fRxIXD/N3MX6K0E9FEveQA1bJ -xsVLLryoHPh9VWwabKE/u+3eFEvBVlzJUnAJakjUOtiKOwndSeT/TZTFah1cgrcR -elv0tZHNgIvpPdsfSjEJLsWbUkyCy+IWsQ4uxdsJvZ3IvcqNKbUOLsNPE/ppIvcq -NyUZwlb63iMPpZgEl+N1VUjUOrgcdxG6i8i9yjeTDOESev1bD6aYBFfgywSJOhmu -wM8Q+hki9ypZVg+X0p98N4rHUpyDhCFcRu/48S9mMQmyuHsSEnUKZPEOQu8g8p1E -uQ4YwuX09f2RxDbcAZHENryL0LuIfCdRdkBmdqY6kZh/x1NyunbLxVsvuXTxkssu -X7a8QYIkLW1j0+EK+uRvXkgyCUx8mLAUdOO5kRYT7yb0biLfQ5Q9RK2DbryX0Huj -76sZQpb+5bX9SSaBhc/EMbFwN6G7iXwfUZ4gbAZso/9S+kGSSZDDGwmTgOM3kkjU -KZDD+wm9n8j3EeUGok4Bjg8Q+gCR7yPK15OsHkz67ktPSCwFPTgrIrEH9xC6h8gP -EmVJhmZqEomnq/ddK6err8huM4cXucSmQjd9681/m8+qwca3PikhUevBxocIfYjI -DxPlzU9KmWmZ6kSCvPp/F6Srupctb6iGJC3tm8vmgEV//MEoDRL04tOESeBgKfau -Fz9L6Gcj979E1Dpw8BFCHyHyPUTZyhBy9IZbXkwyCRy8nSBh9cDp879+IclS0I1r -ox966J3vvdnMUnAlqkjUKXAl7iV0L5E/R5S7qjINmZpE4jvkuZvkNFg5nt3WMxyl -aWwq2PTWrz81iVVDH/7XS0kkah304ecJ/TyRHyVKmJmWqUkk7p2y8xo5XWWXKXkg -yaZCL3385gfjic89nkSSmZapSSS+OvvFkpyu6i1L7iRsKjj0rn+5P5b89sOxZHUi -IT+6T05XOcNclQibClfSlx6+I5Z889qxOq+s1NlHt/96Vwz7hb+SMZJ9o5IILn34 -lfeBSdCHH8wfo9KtFMzTp77wHjAJ+vD1mWME85WCHt1+f1lw/1hBr1JQ0Oe//U4s -+LvJYwRFpWCBbn/k3qlMAhe/Ux3FwMXHCH2MyI8TpYshXEU/91AkkB8WyOMThD4R -C2SmZ6oTiUk3/faUdKpw1cgeX8Xqwad/fPbGeSwFHrJIsYdPEvokkb9AlJPiskbu -2XdLVbrKX7a8oSbK2luqWD0E9PvfunUeS4HAWUjUOhD4FKFPEfmLRJmemZGpSSRI -ad+d1emqYGTindUMIaR3/OG7k5gEBfxaVWSygF8i9EtEfpookxhCkX7v+5HAVcMC -V+E/EfpPsUDsCzy6g6VTYXEkO1IMoZ/+/Kvbk0wCH98nSNQp4OOXCf0ykZ8hyjuE -IQzQ7e9GXXOAr0pI1DoI8FlCnyXyVuUZiSEM0l98ZneSSRDiK8lorYT4FUK/QuSv -EuV+Eq/i5ON/VdJV/QODse19eM4r17380vO7f/7ejIYZQLD0H6UkLZX+oMmlbco/ -k5avkYWkYTYksVS66dvf/P1sWir98dvf/P1suVRKK18nLd8gDbOgCkv7tj8fjT7w -qeejUVn5Jmn5FmmYBiks7Xvqrfc/PJmWSl986/0PT1b2kYZZIGGpVPrbhx9++OGH -hJYeGP6oPEeMhCEFBdPihtQ9FPLAqDNdV1hZ0X0lt8LAkCxR9EKj1nRdYWVjyTrH -KwZ8RKQ2/hoUTIsbmu2EvcVuzRJ5vc81i0FBBKFuiXzB50Gg97hmyDWPD6wyg3Cl -ZxlX6L0iz/VeX+TF1bzP1C+4eM0GXYTcbbaE63IrFL7ez72c8PVxqNZ7zCDMcs8S -Oe5rtjA6xoNHUYcBCr9RczwnNLYeZ1w5HrtuC6N5PBSt5wNbfCfkvmEcmVH7aqeg -KWos3Kht8Z2QGxceN/T21U5Bt692CpotjI0VUGzf7DE9Uy8M+SKwRIE320K3RY67 -oVnwRY/ueCH3PdPVCwVf9GiKWvBFj+Py5UXHzXG/Uetxi0Gv4R1dAkzQvF7wRSg0 -WxhbK+icoJJD+MDzTrhOWGboCO84q14nrGCV8DeFptVnXHFMqFdwNzTP4WZhgy96 -HHckT8o/hcII/v9EIcfd0Mz2crOg2cLoqlgIHxUKTVHLaMsOlF052g3ko+zpwyDP -+Ih80RR1Ew8CR3iNWq6YL1TQfLTL8JDYgtiIZgtjwUekcCWkYsEVZm6FGZpGywQ8 -8XnAQ2PhBAyFZh/f5JmFoFeEwQHb7qHcqYS4KTT9UOspelar0Vkx77DlQ1F7zCBc -zb1GzczllrvC6pvIxJWeta61UVvpWSLHjYuPLvMPB053eT93WzVbGGdWhOlw4mNK -TxAKn68yg3C8/lROtlwRcKNtfFaH60VXNGliNSaec3yLQVQVjJP8ohc6eV6uGVFm -dG24cHit94ytFbbQC322nhc53Rau6dma8G09FMK1ek3HW9rforVorc22aNWMVq1N -cx2vONhs5nMd7XrgW/oB5sqFyhbG+RUcHip7x9a2HO8xi27YJVyXW6HwDwLePRb4 -+LqccdgeBX3WR4DWFNUqFMsb6AjU4aW+5TgiHGncjgVWFPuj3EcM44TuYo8jtPV8 -YCM3c9zf5Fx9iFJxrOkTGYn/arYw5le42uU6Vt85ohhw3epttkW5ARkBZFx0dIQf -Wq/uc7Pc8B4OhOtYfb2iGPBmW+j9hpZzTPdou9xKEGP06pbwPM0WxtkV6XgEeU1R -R1U0RqjKpSA7NlrjWzNHsFUBdmxlP8IsbQXvMYtuuMIx3U2hb4bcHjIWHbV3ByyI -I5k9kBjTuqro+PwoDW9wPNtwKvzN89B3LDH2oMUHC8IPua+PxqQQbxijI73aej7Q -NRKzZZbFg2C9GTr9vEu4xbxn+oY1ntiNqhzJzYON6aZl8SDIepGBrFW2oNnCMI+H -P5pX6Ux5d1xZpsHYOJ5FOg4/hkU0WxhbKlLnqMOgWT43Q75O2MEI2AuOE+k9phUK -f0izhXGOLTRR4F7IXZ7noT+kORXb+6hbXSNoVhU9q+LrCJOXjwfc8Dl/XEZHrUdV -ZlxzNEWNGg3uN1aANDYdeXbA/X7H4qNV3/bNQq+mqMP+rxc53qh1Fx031yXyBeFx -LzR6j5vHh7aveyLHA80WxnlHjtKhp2uKuto3C71jcQf/88Bj+qId+GiALy86bs5Y -e+TULDsdnTP8fsfi8b3OysGQe4EjvGCZl9vgFLjreNy49LiHati8LYzG8QFdzweM -84/Mhwi5awlXU9SK9i3gYbHQJbwexy760T3ESAoGxuXHzbGy7QN7u44ju1aecyDe -jUVvvPPW84Eukc+bXq7cEhy/OJWh6VZZfxSn0X05KPS0tumW6PZNTVHLIBo1Psit -YsiNdROh9ZBaKw2rFXX5MIZXxoa7jMZxyxon5E3H0/yit8YLuW9aodPPjSXjyXQr -nxvbGORNJ+rqIPrgFz1j5TFoyoqwl/uBZgtjUqQwbzqeMdkveqGT51redLyD279j -bdbL2vWCLyzNFsapttCK3dzXhG/rV5sF/WqzYAmfRx2BKHoh9wPjvIktoEPp0wMz -X3Dj2t98OJvr+cAmM19wub/FCXvPL4SO8AJj1ZHXSXmT0UerszesJrdO2Db3y+vG -Hk/Ux1V8DzJZYTw6OB9xQzho9sGAt/hmoUv4XOspepYxviwbRX4A/yJmUbOFIY8Z -0+KxqFkxCwV3yJg3JjKaosYUNmqVEVkxMSYPwOMK2+a+Zgtj6bHSZCw6CqLXRfaN -tolOXc8HjE5bCNvlWsUtS3Sj3l3sGW2LehyX53hgaR4f2GgOrHJcbuQmRNj4beg5 -HlhZx3PC+Cg3EXhRA8H9+L+xbexGc6Rz5gTM6FFfNSF04VCBa+XnI/H//xF04VCB -j6K7zHJFMaeVybdEXreF7grbdjxbNwtOvzH8rdCt9Tguz8YsZMsy2X5j5KMV9SHZ -KDWy0XO0vgksmAkAGQZYtqgVujVbGMZEXIlyxzDqy3VBy4k1nhO2GlPKd5DlH4wm -yx8qhEIfXNCySC/0OYOaoq4387xRW+W47ipf5DeuWL+JX1XknsUPbp6PtWyNtR5D -sIUxu2JEK5h+wLu4Hzo9jmWG3OBjeT+uOEw/iHeyA0BsGAui8mFw4ImBHtfs47lu -3RYj3zSfm7muZV3cD4OjfV5yGNW6sILoOdMBnd1hYETZ0GacXEmqonZxP9wghNuo -Lcvlurh/iFPd8eTV4n6YLQjharYwlvQtDDRH6GbByZtWr+Nxf0gv9Nn6cH4q6iar -l+d5o7YslzvXEwPe5qEC3+KEvevNPDc2j83EI+xrH2FKD6xeno9OM50fIXoYVIHR -NDpR73HFgCW80Beu3t/azUOzVTMrfAiMC8em77jAH0qx7nPbCUIeX0Z/hKMj6Edu -CZblcptFzLNx2dg1NS5QhwxemdFsVCNibI1lZi3X4V7YbAu9r9jNfY+HPBgOQJSl -LcbWiQM5gtYDGKq8PrbF8L11d7GnfIG9sczm5qECN7ZMAEfFOegQevWCLwrcD534 -RmNemQuz4OhmMRSBZbqOZ+v9RpwsR8/DYTTqNve4b4Y8Vy4lS8r1cHwNj6Kuclze -qDmeE67glhsY6w/wV9gu121PBKFjNedFjruBLgrcMwtOf1tcVEe+Zs8vcG/ZhjX9 -bZV19KD9/Ai5N37T+oitstut458bOau1GGHFFO71i6GCLwaHdFs0l5d4c8E1PR6P -6VHvkTdDq5f7+rDv0VA2HCrwbHko29+WDUIzLAZZS+R41vEKxbCSj49PYC0eC0D9 -IBRlprqORWvMXaexME6NI2dZEPpB5cWl7YsBo28i/o8jlYPQD/Qg9B3PDrJFLzB7 -eNYWrUb0JHv5eDVUwlxWKHAvt6rouuvNPDfOHI+OkROEeeDkcQEYmayoK71ivlEr -ennTD3pNdxPnOWPleGio0HEeDwLT5mPVTBRKvDEcqOOqCeVO3OLqop/7rjBzY1ZN -PJodHs32t41+rujDhyZQMo4R3QjS8kpZdYz64sXSYszxuc0HC3ow5IXmYPnNifht -JscLwuPfeh9gL3qH0YmWkTHtQCRdIl9wXG5MiSdoZcmDz27H2iv63OaDhfI/zRYj -FocRZMpD5xWDcPi3UyroL/giz8NeXgyia7CeQIsOaeN6cjR8yzJaxw/SpudzQWhG -J/JKuf78gOlz3Rb9eZF39H4nbyzQw6ECj81rHa3tHcbWsdEbX4E7vO6o1ASaLYyT -YlYOlTiWWTBOKdN6iLwajuTcCg7jfSx+70IvP7CMUnRC/eFHKNRdbpvWULb89FWz -hdFUwbw5EOjmQNAc5PqabaGbA4HOvVxBOF4Yc3q079UeSbFefs0l4rTyfZtBPeSD -oR69det4tu6Ynul4OT6o5c1CHOKJ9awV5fCwuvXQ7HZ5hKVxPPJxiFoO4N2yhnRb -NF8ZCG/0lZ7IDe5rjueEWsvBi3h8eXlk5cObSfTschyYcvwATAfdjR8TprLyYUya -LYzTK2gazNnNtij3BgWfF0ajahgXHN2qPUhnRTQr96uD5OIoTh0+Ajuh2R04nhMe -fIt3zHtt/DBCd3pMi2u2GDUaWL0853hOOHpV5IctWVsc/NrD8QJhBvls9NaaFhg7 -yFgrmmVavVxfy8Plvul4gb7GC7nrOmvX5LhptBjtWqse5gv6arHO9HJ6NptttUV8 -9MzG6yFriXy2/E5IVoTczY4858ta+dzYnwpDvjCUlkUdnR2dVmfbgpbWXK6zc0Eu -t7CD8+5Wo9vI5Vq4YVim1Wp1GqfqxcDXXadbH1zYke1ob45fw7O9ou463ZYWCK3D -UDpyHe3drZ093abVuWgR71iYMzt5d27RooULcrmeRd0LrO62zgW8w9COoC7X7Dpe -cbB5cGFHc0e7FgjNMJS2BW28tbvD6m7hvNtqWWj1tPX0LGw1uMkXmgtau9t5T65j -YTs3qi/tzwXiciN9aX8wFFim617+/wYAV467yt1DAAANCi0tZGE3ODlhOGIwNjlm -ZmU1MGNiNjlkZDgxZTgzNDU1ZTZiODc2MGU2NTNjYjUwMjQ2Mzg5OTY1MjVkNDI2 -DQpDb250ZW50LURpc3Bvc2l0aW9uOiBmb3JtLWRhdGE7IG5hbWU9InNhbXBsZV90 -eXBlX2NvbmZpZyI7IGZpbGVuYW1lPSJzYW1wbGVfdHlwZV9jb25maWcuanNvbiIN -CkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtDQoNCnsiYWxs -b2Nfb2JqZWN0cyI6eyJ1bml0cyI6Im9iamVjdHMifSwiYWxsb2Nfc3BhY2UiOnsi -dW5pdHMiOiJieXRlcyJ9LCJpbnVzZV9vYmplY3RzIjp7InVuaXRzIjoib2JqZWN0 -cyIsImFnZ3JlZ2F0aW9uIjoiYXZlcmFnZSJ9LCJpbnVzZV9zcGFjZSI6eyJ1bml0 -cyI6ImJ5dGVzIiwiYWdncmVnYXRpb24iOiJhdmVyYWdlIn19DQotLWRhNzg5YThi -MDY5ZmZlNTBjYjY5ZGQ4MWU4MzQ1NWU2Yjg3NjBlNjUzY2I1MDI0NjM4OTk2NTI1 -ZDQyNi0tDQo= -******************* -HEADERS: -User-Agent: Go-http-client/1.1 -Content-Length: 1979 -Content-Type: multipart/form-data; boundary=8bed23bab43c5ac24b097e61a30ce57a57a9980edcc90abf588af69cb186 -Accept-Encoding: gzip -BODY: -LS04YmVkMjNiYWI0M2M1YWMyNGIwOTdlNjFhMzBjZTU3YTU3YTk5ODBlZGNjOTBh -YmY1ODhhZjY5Y2IxODYNCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsg -bmFtZT0icHJvZmlsZSI7IGZpbGVuYW1lPSJwcm9maWxlLnBwcm9mIg0KQ29udGVu -dC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0KH4sIAAAAAAAE/7xW -X2wc1fX+7T/7etfxHq//XS8JGSbg3F1+ntm9tnfXVdU/adQmqIggC1EVwXJn5u54 -6pm525lZy+7TPFAFAhJIpZVKmyqIqAgqWvKC+hRFVV94KVKkFoRaoOUBEKhqIVWV -ljbVnfUmrlGpTGP2ZVbz3XO+7zv3zLn32JMvPfP2I6dfvTyTz6IUpPNZlIHsiQe/ -d/518fXk//3xc3/IqjMohS/85aG0mkMpuJJRcygNP8xDqlpAKcjhnDJE/pSpFlAa -hvGwgsjbIyqgNL78VhKSgZdQsjYDI3hEQeQRpALK4D9eflBmzMI7/VRZyOO8UiAv -5lVAWfzO909LOAf/yCTRORjFo8o+8lpGBZTD5z5IoofgxX70EIzhMaVA3h0pjUEK -pO58LpXOZHMqoCH8Xl9+Gp7JQ0oFNIzP/i1JkIXf5CG1LWYoPZzNqUWE8BuXTqfV -LBqGw5Cq5tEwFHFRAXJILaIR/P7DP86pWYRgJQERjONxpUSOqYDy+NLJszk1h0bg -XCrRPgITeEKZJD9LqUVUwG9efCqnZlEe7CQ2D1N4SpkmWC2iUfzP3yWJC3BnAhZg -Bs8omDRVQPvwa5eelolH4Vw6STwKs3hWKZMn0yqgMfzAnxN4H5zuw/vgBnyDUiYf -SstF/J03E3gM/tqXNQb78X6lTD6QMOAXXkngIjzRh4twAB9QyuRxCY/jn76ewAA/ -78MAN+IblTJ5IVWaGFQ8j0byhdF9Y0UYVwGV8MuP/0hu4jicySZ6x+EgPqgo5Ims -WkQT+LsXT8oKl+DzidcS3IRvUlRSUafRJH7u96ckOAGfU3NoEh4bStZMwCF8SLmZ -fLZaQJNwC75FmSO/lXs8hX/1yilJNgU/ySVkU3AYH1bmyLMSnsZnX07gaXi2D08D -wUSZI09JeAY//fwPZPQMPNzvtxmo4IoyRx6S/YbxoxeTaAyv9p1gqOKqMkcuZFVA -s/jkrxN4Fh7rJ5+FW/GtMjpXKg2KM1KamJzKTs/g2fIUSkH8RpzGcfyupsT3k/+v -zbdS5TJKQxyf+sX5t/bjOH5PPpU4zhOtppdnUQbiCw/88rzEzjwqn0qskFqtXp5E -WYgvPP/+h1cO4jg+J5+ElssoB3Ec//2K/KVwfGbwlyzQ/6PDIfO6Lg9pzhQ9P6IZ -s9ujBZ/5IuSm8K2QzgY9P3I8rq1y1j3iRKHm843oyyyMqKGvCo/rq4HwxLf4GtNt -oXfXbN0Tlm4Ll/m2JgJbj4RwzVXm+F9Yr2k1rT5vi7pG69qC5jp+b2OeeVZjUQ8D -Uw96vqTSPcOJPNbVbEFLwRZ9aDJfGN/gZrSHvLbpsWBN8hYHvLZ5NGCOT/dfe3HE -vp0Fa3eLYI0HWqfnm5Tep+9VKWxTypkYsIebYcS9MGLmGjX3ipSFXpt5VmNRC+n0 -gNn+N9/XCuRxzxPrnNp7pWaL4Kqi220nWu0Zmik83Q5Yh/lM724GIjRFl8/bQreF -xd2IdQPR0R0/4oHPXL3bDURHI9VuICJh9DoVzfGjxmJIv7lT951fO35CFxF3503h -utyMRKCvc98Sgf7JmfXuFq/czZVPnqfvoOO4/EjPcS0eVLSV5BOm/qfoQ5q4738y -cZS7ETvGWfdEkLgJKtrdgRPxrVeRoOGn4ydplfYq74+bL+3ClEaqW2rldgQVbcvK -R4fBdeqogcjP/BeRGqmu8DB0hF/RrJ7X3aaS3nUdqxr2SWQzLO1CUq/rCmYdZRGj -tV2EBTzkEW3tIiJia3zFZ91wVUQhbewiciViQZSM9vq2QcesdSfk1NlZwut17IWb -Ydt1/N7G1VG3fezf5fdCbt2xQq294ve41+eXOzo+mPvh5hb1R/v6ehn3uCcplQEl -qXaZzb/ousKsyIN/nfs2v8PntLNn1m0zNPs8Usmhj1Wy1RgHPnYRvfkaPEgdrEQs -4hXN8Z1+d9HtSXauCnr+teuPYQ9g+u3UzipoJjNXuX4bj47Iu0qoH/cj7rrObcct -zmiNLmp1PfK6+lfEV5lv6e12u26LttFzXKvd/yrapvDaHo8CxxRtefa1r559bdOz -dr6Sxy0lteVGs9E0mwtLtbplNZtLltVqcG7UqUEtq8YpNZlZN5v0kN4LA911DH2j -1Wg3Fudd2eXztt/TXccwtVBoDUoaVmPRqDc7BjOby8u80bJYkxvW8nJrybI6y8aS -aSw0l3iDavp/Tmdt5d5oNebl/UVolJKFpQVeNxqmUePcMGsts7PQ6bTqlDPeYkt1 -Y5F3rEZrkdOhe9atUNxL8/esh5uhyVz33n8FAAD//wwi4sIeDwAADQotLThiZWQy -M2JhYjQzYzVhYzI0YjA5N2U2MWEzMGNlNTdhNTdhOTk4MGVkY2M5MGFiZjU4OGFm -NjljYjE4Ni0tDQo= -******************* -HEADERS: -User-Agent: Go-http-client/1.1 -Content-Length: 7276 -Content-Type: multipart/form-data; boundary=281cf637734b8fe81951ee3509cef5706ca07ed3a6c85806c2413fc5e676 -Accept-Encoding: gzip -BODY: -LS0yODFjZjYzNzczNGI4ZmU4MTk1MWVlMzUwOWNlZjU3MDZjYTA3ZWQzYTZjODU4 -MDZjMjQxM2ZjNWU2NzYNCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsg -bmFtZT0icHJvZmlsZSI7IGZpbGVuYW1lPSJwcm9maWxlLnBwcm9mIg0KQ29udGVu -dC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0KH4sIAAAJbogE/7x6 -eXgc1ZWvbqtLOm4h6bjxci1v5QuGUlmqkkqLJRaDLdvYxpZXMMY2Tan6qlS4um6n -qlqymCUNsdnigIclZnPA4MEJEELCC0y+ZBKTkIHvTZ4zywsJ2zAJzJePIWGAYR6Q -EHjf19WS2rKRJdsz/0jdfc8953d+59xzz71VV8aBYOzqfF5OxKEc44k4SBhLxKGi -8LkSY2w6EPqzHz0sMQkI3lnJJIjhrwkStQoIAgV5knJHpVoFMUzQhFylPEYYQow+ -91FhSjkeiGTL8Qx6hlytfI0whHL65k0HJSZBHJ8hSNQqiGMNrZGrlW8RVgtx+sY/ -PSyxOEhoI1ETIGEtrZVRoawWJPrpa49ILA4VuB6JmoAKnEwny0llIUOooP/ywSGJ -SVCJ344hUaugEs+kZ8pTlAMxhlBJd713SGISAO6PhgGn0qnyFOUTwhCA3vHGIYlJ -MAk/jGBNwml0mjxF+U/CECbRp391SGISJPDeaDiB0+l0eYpyJ2EICfrE64ckJkEV -/k00XIWUUnmK8jRJzkpWxB7/g1JWlkiQWHlcqqiESYm6CojR/GFkCFV033N3S0yC -M/CeCiRqFZyBM+gMuU75UgWrhTPou9+7X2JxqMYZBaercSadKc9SZjCEanrHo4ck -JkEN/mNkuAZn09nyHOV5wmqhht53/yGJxaEWg8LcWpxL58pzlA0MoZb+4rXCXMQf -ESRqFSDKVJanKH9LknXJSeTu61fFvr3rTpKQqs6orqmtq4AYzecvZtMA6c037Wpk -cZiMnUyCJN4XQ6ImYDLOo/NkpuhqFSTxLHqWfLZyb4zVwmT6wsF/aWRxOBM3IlET -cCbOp/Plc5RzGUKSPvbTVxuZBFPwZYJErYIpeC49V1aUlwhDOJNe/8KrjUyCqfhf -BIlaBVOxntbLivI2YQhT6EPPvdrIJJiGbxIkahVMQ5WqsqK8SBjCVProz19tZBJM -x4MxJGoVTMcFdIGsKNfFGMI0uvvZVxuZBBSfJUjUKqDYQBtkRTlMWC1Mp//5/BuN -LA4zsAWJmoAZ2EgbZU1RWS1QuufZNxtZHOqwBYmagDrUqS43KQqrhRn0xRffbGRx -mIlzCtTPxGbaLBvKLFYLdXTPrs8qWRxm4Z8XBmdhC22RW5WdDGEm/cE7n1YyCWbj -7lghLrOxjbbJrcrHhCHMot8/8HYDk2AOPhuFbQ6203Z5ofIdwhBm03/e/+8NTIK5 -+EA0ey520A65U7klxmphDv35z99qYHGQcSUSNQEynkfPkzuVDoYwl/725d83MAnm -4UeR6nl4Pj1fvkB5g7BakOmhZ3/fwOLA8IuFuQwvpBfKFyirGMI8+vyeDxqYBGfh -96O5Z+Eiuki+SHmAMARG3/vqBw1MgrPxY4JErYKz8WJ6sXyR8hFhtXAW/bvfftDA -4jAfz0KiJmA+LqaL5SXKDIZwNn1l77sNTIJz8JeVBULOwS7aJS9V/r2CIcynX3vo -3QYmwbn4CiBRq+BcXEaXyUuV+4EhnEPfv/vdBiaBgrdBYbaCy+lyealyK7BaOJe+ -etMNGotDPc4rOFWPl9BL5BXKXDYVFHrwxhs0FgcVK1gcFmBdQUTFlXSlvEqR1AQs -wEvppfIKpYYh1NN7f7E3xiRowC/HkKhV0ICr6Wp5jbKXJBuSFWVl8UN3nZ2ow8nJ -M6dMnTadzqibOWv2nLnyPHbW2fPPOVepj8pCHtg0UOlf7/44xuLQiPVMAg3vJ0jU -BDRiN+2W1yqKWgUarqPr5LXK3YQhLKB3PfNWA5NAx7sjinVcT9fLG5R9hNVCA939 -9FsNLA5N2FBQ1IQb6UZ5k7KA1UIj/ejJd8pZHJrxnMJgM15GL5M3KfMZgkbvve6d -ciaBgY9Hig28nF4ub1a+QRiCTr/23bcamAQt+ABBolZBC15Br5A3RLWxid76VAFW -K95KkKgJaMUtdIu8QelntdBM9+9/q4HFoQ0XFQbb8Ep6pbxBuZDVgkHffaaYcFci -SZ6drCgrIzde35RIqgsaGjW9qdkYzdwsVgst9E/PPhRncWjHWUjUBLTjVrpV3qbM -ZLXQSve9eiDO4rAQncLgQtxOt8tXKVsZQhv96MYfxpkEHXhLhLUDUzQlX60MMoR2 -+uGTB+qYBJ34ldZCeDvRpKbco3xVZwgL6ROPHqhjEpyHd+lI1Co4Dy1qyT3KXTpD -6KC//MMdMSbB+fh/o9nnY5qm5TXKM61sOnTSf35xb4xJcAE+1cokaBhaJxcgp1xe -o3y7NTkjWVlWdvukf71OTlS2tLa1L+zorJMgRg+XM4Tz6K4jB8qZBBfivmjmhdhL -e2Vb2UsYwvn0Zx8cKmcSLML/qECiVsEi7KN9sqM8WMEQLqCf7P16OZPgInypEola -BRfhNfQa2VF+VckQLqRvv/GYyiS4GD+uRKJWwcW4g+6QXeV3lQxhET3yzmMqk2Ax -PgqF2YsxQzOyq3wDItT/Z8Y/5OVE5XnnX3DhoiLqBwlDuIj+8VdF1N8kSJIzklBW -ls/HbsrLicqLitJxiFFkCBfT2x5/pJxJsAS/SZCoVbAEPerJIkrExfTLbxZB/qkS -SXJ6srKsLC8fzssJ6eLFi4pk5QlDWEL/8fYjCSZBFz4RaerCLM3KX1DuJwyhi951 -45EEk2Ap3hml9FL0qS9/QdlDWC0spU/88ffVLA7L0ECiJmAZBjSQQ6WZ1cIy+utb -f13F4rAcZxcGl2OO5uR+ZSZDWE73/9drNUyCS/C+SPElOEAH5J3KPSRZl6woK6t8 -/RGWgCVdS5ctW97RGdWDw3GGcAnd8zd/KmcSrMBvxZGoVbACB+mgfK1yIM4QVtDd -R+6qZhKsxMej4ZX4Z/TP5D9XHo0npyUrysoqHn2BJeKXrBjSmpfYVFhJX/7JQ3EW -h1XYwuLQXsS8Cv+C/oW8TWlmCKvoL1+/sZZJcCm+uwiJWgWX4l/Sv5S/qHzWzhAu -pUeO3FjLJFiNn7YjUathNeYJzRP5i8qn7ckZyYqyslnffUFOVK5sbVt1aUdnXQXE -aH4vYQir6Yf/9r06JsEafJ4gUWtgDV5H6HVEvp5EkVhDj/ymINEdEVYD3fglQr9E -5OuJch+Jkqu4JFa2tq1eU4zy4XI2Fbrpr26/P87isBaXsTisQw2JWg1rcRehu4i8 -mygXqtWwDm8g9IbC13o2HdbS+3Y9HGcSrMdb4kyCDXhPDIlaDevxRkJvJPI25ea4 -Wg0b8CZCbyLyNuWWGENYRz9+5OE4k2Aj3lCORK2GjXgzoTcTeZvywxhDWE9vfP+h -OJNgE75KkKhnwCa8hdBbiLxNSbFa2EBf/OlDcRaHdpyDhCFspPt+8cpMJsFluH8S -ErUGLsMvE/plIu8hyg3AEDbRd468MpNJcDnuBiRqDVyOXyH0K0TeQ5TdkJyVrCgr -W7DvSTlR1b123foNrW0bN3V01kkQo/mr2TS4jD7xxvMxJsFmPEhYHK7AS5GoNbAZ -byX0ViLfRpQDRK2GK3AvoXuJfBtRLmEIl9P/99aRGJNgCz5NkKg1sAX/itC/IvLt -RPkmYdNhM/37/M9jTIIr8WbCJNiKfxtDotbAlXgHoXcQ+Xai3ETUGtiKdxJ6J5Fv -J8oPYqwWrqAfvfRNicVhG85EolbDNryL0LuI/FWiLErSZGVZ2VMVh6+XExWXXb75 -io7OOgliNC+xybCFvv/ePyxgFbAd3/+KhESthe24j9B9RL6bKO99RUpOTVaUlZHX -/6ktUb5lKAsPn83mwJX0F5+O0CDBVfgUYRKkMB9l3FV4D6H3FOj4DlGrIYX3Enov -kW8jyhaGsJXetOeFGJMghXcQJKwWttHnfvN8jMXhClxV+GE7vfvj9xpZHK5GtcDw -1XgfofcR+X6i3FOerEtWlpX9hDx7i5yAK7duu3zz9qEoTWWT4Sp66w+enMQqwMT/ -eClKRRP3E7qfyF8jSpicmqwsK7u/Zu91cqL8qmKRezDGJkOKPv7lh6KJzz4eQxJJ -fm/WC3k5UZ4qSu4lbDJcTe/5+wciyR8fjCQrysrkRw/LifKrh1ZsnrDJYNKXDu6L -JN+7frROs5hcBZ09dNdv7opgP/8HMkqyZ0QSwaIHX/sEmAQmfrpglEqrVDBNn/zW -x8AkMPGdGaME06WCnO56oCh4ZLQgLxXspc/9+MNI8N/OGCXYWypo012P3D+ZSdCD -P6lAolZDDz5A6ANEfpAoXQyhj3794YKANSRg4QFCD0QCUQWedMub8xJxu2+I0cPl -rBYc+rtnbp7P4pBGhkSthjQ+ROhDRH6YKLOjPYzcd3hPeaLc6eisqyzM21POauEa -+rMf3TqfxYEX1wrHg4QeJPJfE2VacWL+8N0VifJrhifeXcEQdtB9b/90EpOgF79f -XvClFx8h9BEiHyLKJIbg0r/7WUHAHhKw8euEfj0SiHyBR3ezRHyHO+RLPs4QMvTl -7+2KMQn68BOCRK2BPvwGod8g8qNE+ZAwBI/u+mhvjEng4OsSErUaHHyM0MeIvEZ5 -WmIIgr7y1f0xJsE1+FqsoOIafJzQx4n8TaI8QJJTkxVlZbHH/6AkyjOeKPKIK772 -jZ/+70/2v/zx9LrpQDD/r/kYzeff1uT81coTpOlbpIPUzYIY5vO3/PiHv51F8/nf -/fiHv50l5/MJ5UnS9G1SNxPKMX9413OF0Qdve64wKivfIU1PkbqpEMf84Sff/+Sz -uTSf//b7n3w2V/lfpG4mSJjP5//42WefffYZofkHhz4q3yVGmSEFWdPihtQzGPLA -qDZdV1gp0XMNt8LAkCyR80KjynRdYaUiyWrHywV8WKQq+hpkTYsbjbYT9uV6NEtk -9B2umQuyIgh1S2SyPg8Cvdc1Q65184HNvhNy39ii94kM1/t8kRHX8h2mvv6Klet0 -EXK30RKuy61Q+Ho/99LC18ehWU/zyIItDGNsJPa1TlZT1AhHvbbZd0JuXHba4NjX -OlndvtbJarYwNpRAsX2z1/RMPTvoi8ASWd5oC90Wae6GZtYXvbrjhdz3TFfPZn3R -qylq1he9jsuX5Bw3zf16rdfNBX2Gd3LMTdC8nvVFKDRbGBtPqw8bzUzW5cZVp0TM -Uu6G5gpuZtf5otdxh8NY/CkURvA/Q1Kau6GZ6uNmVrOF0VWSeCeiW1PUItqiA0VX -jKtObmGcyJ4+BPK8EzCvKepGHgSO8Oq1dC6TLaH5ZFfJcbEFkRHNFkbbCZKhFFIu -6wozvdQMTaNpAp74POCh0VFSo44HqtRQaO7gGz0zG/SJMDDaJzBzY2j6odab86xm -Y7af80Inw4trujDUte6yoWD3jk5TW+jZHbaeEWndFq7p2ZrwbT0UwrX6TMe7uL9J -a9KaG23RrBnNWovmOl5uZ6OZSbe36oFv6UeZKxYSWxhrT+D46NqT5r1mzg27hOty -KxT+McB7RgMfX/k+HumjbI+AvuAEoDVFtbK54goahjoU682nEeHwjnQqsAqxP8lE -Mowze3K9jtC6+cAGbqa5v9G59ji14lTTp2Ak+qvZwlhQ4mqX61g7VohcwHWrr9EW -xQ1iGJBx+ckVruPr1X1uprk/FgjXsXb0iVzAG22h9xta2jHdk20rSkGM0qtbwvM0 -WxgXlaTjGPKaoo6oqC+gKtaC1OhojW/NjGGrBOzo0j7GLG0p7zVzbrjUMd2NoW+G -3B40Ok/au6MWxFhmjybGtL6Qc3x+kobXOZ5tOCX+ZnjoO5YY3UHynVnhh9zXR2JS -7KpGRvq0bj7QNRyzxZbFg6DbDJ1+3iXcXMYzfcMaT+xGVA7n5rHGdNOyeBCkvIKB -lFW0oNnCME+HP5pX6kyxOi4r0mBsOE1+DLmq2cLYXJI6Jx0GzfK5GfLVwg6Gwa4/ -TaT3mlYo/EHNFsYKW2giy72QuzzDQ39Qc0rK+4hbXcNoluc8q+TrMJPbxwNu6AAz -LqMj1gu7zLjmaIo6fDwYwWxsHHt2wP1+x+IjO6/tm9k+TVGH/O8WaV6v9eQcN90l -MlnhcS80+k6bx8e3r3sizQPNFsaasaN0/Omaol7im9m+0biD/37gEX2FCnwywJfk -HDdtrBo7NYtOF1pUv9+xeL3meE64bGfIvcARXrDYS69zstx1PG5sncgZcVyQh8zb -wqgfH9BuPmCsHVu5CLlrCVdT1JL2LeBhLtslvF7Hzvlm6AhvOAUDY/tpc6xo++je -rn1s14pzjsa7IeeNd143H+gSmYzppYstwemLUxGabhX1F+I0UpeDbG9zi26JHt/U -FLUIol7jO7mVC7mxejzbwlAxO67WUsNqyb78OYaXRYa7jPpxyxpnZkzH0/yct9IL -uW9aodPPjUXjKUpWJj26MciYTqGrg8IHP+cZy05BU0qEfdwPNFsYkwoKM6bjGWcU -D3/Rt2Pav1Nt1ofPer6wNFsYZ9lCy/VwXxO+rV9rZvVrzawlfF7oCETOC7kfGGvG -w9dQpI+vTw/MTNaN9v7Gz7PZzQeiqx5/sxP2rc2GjvACY/nY66RYZPSR3dkbUpNe -LWyb+8V1Y0/MizF2/GNMlhgvHJwnNvtYwJt9M9slfK715jzLGF+WfQ7/ImJRs4Uh -j4qNFo0VmhUzm3UHjfmjIqMpakRhvVYakaUTY/Ko/HKFbXNfs4Vx8anSZHSOXXmP -CZPm8YHVwra5b7SMXbSPndrNB4yFthC2y7WSW5bCjWdPrnekLep1XJ7mgaV5fGCD -ObDccbmRnhBh47ehp3lgpRzPCaOj3ETgFRoI7kf/jasntMQngLDQV00IXTiY5Vrx -/jr6/9+CLhzMcr0n57iFfW+b5YpcWiu6ZomMbgvdFbbteLZuZp1+Y+hbtkfrdVye -ishOFWVS/cbwR6vQh6QKqZFyPCc0dkxgwUwAyBDAokUt26PZwjAm4kohdwyjtrgv -aGmx0nPCZqOmeClY/MFosPzBbCj0nW1NnXp2h7NTU9RuM8PrteWO6y73RWbD0u6N -/As57ln82Ob5VLet0dYjCLYwZpWMaFnTD3gX90On17HMkBt8NO+nFYfpB1ElOwrE -utEgtNLOxhMDva65g6d7dFsMf9N8bqa7FndxPwxO9sJ8WNnRqnVhBYUHDUd1dp8D -o5ANLcbcUlIVtYv74Toh3HptcTrdxf3jnOpOJ68W98NUVghXs4WxaEdHoDlCN7NO -xrT6HI/7g3p2h60P5aeibrT6eIbXa4vT6Us9MeBtGszyzU7Y121muLFpdCaOcX92 -AlN6YPXxTOE0s/AEop+DKjAaRibqva4YsIQX+sLV+5t7eGg2a2aJD4Fx2ej0HRf4 -4ynWfW47Qcijy+gTODqMfvgh4uJ0epOIeDa2jV5T4wJ13OAVGU0V9ogIW32RWct1 -uBc22kLfkevhvsdDHgwFoJClTcaWiQMZQ+tRDJXeYdti6N66J9dbvMDeUGRz02CW -G5sngKOkFBxHr571RZb7oRPdaMwvcmFmHd3MhSKwTNfxbL3fiJLl5Hn4HI26zT3u -myFPF7eSRcX9cHwNj6Iud9ziFcNSbrmB0X2Uv8J2uW57IggdqzEj0twNdJHlnpl1 -+luiTXX4a2ptlnuL163sbyndR4+p52Pk3vhN68O2im43j39uwVmtyQhLpnCvXwxm -fbFzULdFY3GJN2Zd0+PRmF7oPTJmaPVxXx/yvTCUCgezPFUcSvW3pILQDHNByhJp -nnK8bC4s5eOLE1iLpwJQPwZFkamuU9EacbfQ6IhSY+wsC0I/KL24tH0xYOyYiP/j -SOUg9AM9CH3Hs4NUzgvMXp6yRbPRrNnCWDJeDaUwF2ez3Esvz7lut5nhxvnj0TF8 -gjCPnrxkQpMVdZmXy9RrOS9j+kGf6W7kPG0sG48TwwAUdQ0PAtPmo9VMFEpUGI6G -8oUJ5U7U4uqin/uuMNOjVk00mhoaTfW3jHwu6cMHJ7BlnCK6YaTFlbL8FPVFi6XJ -mONzm+/M6sGgF5o7NUW1RCbruNyv1xwvCE9/632UvcK7WU5hGRlTj0bSJTJZx+VG -TTRBK+I69ux2qr2iz22+M1v8p9li2OIQgmRxaE0uCId+m1dCf9YXGR728VxQuAbr -DbTCIW1cT46GbllG7jOP0aZn0kFoFk7kpXL9mQHT57ot+jMi4+j9TsZo08PBLI/M -a+3Nre3GlpNL0M/XXdhqAs0WxuyIleMljmVmjXlFWo+TV0ORPLuEw6gURe9d6MUH -loUUnVB/eAKFustt0xpMZaPXNTRbGA0lzJsDgW4OBI1BekejLXRzINC5l84Kxwsj -Tk/2vaixFOvF11wKnJa+b7NTD/nOUOeeJdKOZ+uO6ZmOl+Y7tYyZjUI8sZ61ZDv8 -XN16aPa4vIClfjzyUYiajuLdsgZ1WzReEwhv5JWeghvc1xzPCbWmYxfxeBuvsZQP -FZPCs8txYErzozAdczd+SpiKyocwabYwzi2haWfabrRFsTfI+jw7ElXDWH9yq/YY -nSXRLK1Xx8hFUZw8dAR2QrMncDwnPPYW75RrbfFxgdNrWlyzxYjRwOrjacdzwpGr -Ij9sStni2NceThcIM8ikCm+taYGxm4y2olmm1cf1VTxc4puOF+grvZC7rrNqZZqb -RpPRqjXrYSarXyJWm15aT6VSzbaIjp6paD2kLJFJFd8JSYmQu6nh53wpK5Me/VN2 -0BeG0tTZvrB9obWwpa2pOZ1euLAtne5o57yn2egx0ukmbhiWaTVbC42z9Fzg667T -o+/saE+1tzZGr+HZXk53nR5LC4TWbijt6fbWnuaFvT2mtbCzk7d3pM2FvCfd2dnR -lk73dva0WT0tC9t4u6GNoS7d6Dpebmfjzo72xvZWLRCaYSgtbS28uafd6mnivMdq -6rB6W3p7O5oNbvIOs625p5X3pts7WrlRsbU/HYjtRmJrfzAYWKbrbv//AwC+zNsT -Vz8AAA0KLS0yODFjZjYzNzczNGI4ZmU4MTk1MWVlMzUwOWNlZjU3MDZjYTA3ZWQz -YTZjODU4MDZjMjQxM2ZjNWU2NzYNCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0t -ZGF0YTsgbmFtZT0ic2FtcGxlX3R5cGVfY29uZmlnIjsgZmlsZW5hbWU9InNhbXBs -ZV90eXBlX2NvbmZpZy5qc29uIg0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi9v -Y3RldC1zdHJlYW0NCg0KeyJhbGxvY19vYmplY3RzIjp7InVuaXRzIjoib2JqZWN0 -cyJ9LCJhbGxvY19zcGFjZSI6eyJ1bml0cyI6ImJ5dGVzIn0sImludXNlX29iamVj -dHMiOnsidW5pdHMiOiJvYmplY3RzIiwiYWdncmVnYXRpb24iOiJhdmVyYWdlIn0s -ImludXNlX3NwYWNlIjp7InVuaXRzIjoiYnl0ZXMiLCJhZ2dyZWdhdGlvbiI6ImF2 -ZXJhZ2UifX0NCi0tMjgxY2Y2Mzc3MzRiOGZlODE5NTFlZTM1MDljZWY1NzA2Y2Ew -N2VkM2E2Yzg1ODA2YzI0MTNmYzVlNjc2LS0NCg== -******************* -HEADERS: -User-Agent: Go-http-client/1.1 -Content-Length: 6708 -Content-Type: multipart/form-data; boundary=236cff23146496fd2cf8a63650d62e2265e008fbffd139bd0853693aa524 -Accept-Encoding: gzip -BODY: -LS0yMzZjZmYyMzE0NjQ5NmZkMmNmOGE2MzY1MGQ2MmUyMjY1ZTAwOGZiZmZkMTM5 -YmQwODUzNjkzYWE1MjQNCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsg -bmFtZT0icHJvZmlsZSI7IGZpbGVuYW1lPSJwcm9maWxlLnBwcm9mIg0KQ29udGVu -dC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0KH4sIAAAJbogE/7R6 -fZgU1ZnvnJ6umZceoF9agcMAUhxAa4qZqpmaYWZQQWAAAWH4BhGxra4+U1Ohu05b -VT0fbnZtDYgiKmuiYMT4ETfuGpN1N3eTmycfi4lZfZ7NJbt3b0z8yM1ukn3yuCau -erM3mhjZ56nqmWlmEGeA/DPT3ef9+L2/857zvudU3RAHgrGbSyU5EYdqjCfiIGEs -EYea8HMtxhgCocdeOC4xCQg+XINErQOCQEGepHyqhiUhRt/++gmJxSGGs5CoCYhh -gibkOmUWQ6imn37maYlJUI3/TJCodVCNk+lkeYryImFJiNNHTjwtsTjE0UeiJiCO -U+lUeYqyjSFI9Ic/eVpiEkj49yT0K2GSJmVUvkVSM1M1VVXk+B0bEhKJVcel+hqI -0VJpBZsBNfTuuw40sTjU4FImQS0+Egtx1eA0Ok1OKbpaB7V4Cb1EvlT5bIwloZa+ -9NT/bWJxANweSgJOp9PlGcoVDAHoF7/3ehOTYBK+GqGYhDPpTJkqrxCGMIne8dLr -TUyCBP4XQaLWQQJn0VkyVd4kDCFBn3zh9SYmQR3+IoqhDutpvUyVlwlDqKPP/OD1 -JibBZHwqhkStg8k4m86WqXJ7jCFMpgeff72JSTAFnydI1DqYgnPoHJkqJwlLwhT6 -/178eROLw1RsRaImYCrOpXPlyxSVJWEqPfL8L5pYHJLlwSTOo/NkWVFYEpL05ZfD -QcTLkKgJQJxP58tMmcOSgPTIgdO1LA7T8JNI1ARMwwV0gbxQGWAI0+g33/qwlkmQ -woMR4hQuoovkhcr7hCGk6DeeeLORSXAJPh/RcQleTi+Xr1D+ljCES+i/PPofjUyC -S/GxGBK1Di5FhSpyg3I4xpJwKf3BD95oZHGYjuuRqAmYjipV5QalkyFMp7989deN -TIIZ+F5kegYupovlRuXnhCVhBn36+V83sjjMxNvCkGZiE22SG5UNDGEmffHIbxqZ -BBS/ERFJUaOarCuPEYZA6TsP/aaRSTAL34+GZ2EzbZZ15T3CkjCL/sMvf9PI4lCP -C0LT9dhCW2QjzPB6+trRtxuZBLPxR7Wh6dnYSlvlNuU/ahjCbPq5J99uZBLMwdcA -iVoHc3AJXSK3KSeAIcyh7x5/u5FJMBfvh1B7LrbTdrlNuQ9YEubS1++6U2NxuAzn -h54vww7aIXcq89h0uIw+dehOjcVhHtawOMhYH4rMw6V0qXylIqkJkPEqepXcqUxl -CPPoZ394NMYkmI/3xJCodTAfr6ZXy8uUoyTVmKqpqoo//eDCRH1NLUxK1E2eMjWJ -01KXXDp9xkw6q372nLmXzSuvMWAzQKZ/cfD9GIsDwwYmwQI8QZCoCWC4nC6Xr1EU -tQ4W4Aq6Qr5GOU4Ywnz64NfeaGQSLMTjEcULcSVdKa9SjhGWBEYPfvWNRhaHRdiI -RE3AIuyiXfJqZTFLwgL63nNvVbM4XI6Xh4OX4xq6Rl6tLGIIC+lnb3+rmklwBT5L -wrCuwLV0rXyt8leEISyin/u7NxqZBAo+Fg0ruI6uk1cpnyEM4XJ631feaGQSNOB9 -UQQNuJ6ul1cpfSwJV9BHH32jkcVBxeWhYxU30A3yKmUZS4JC3/5aOeFuQJJamKqp -qiKH7mhOpOT5bMHCRZdfoYxmbg5LQgP9w/NPxlkcFuMcJGoCFuN19Dp5ozKbJUGl -x15/Is7i0IhOONiIm+gmuVvZyxAW0/cOfTvOJGjCwxHWJtxMN8tblEGG0Eh/+9wT -9UwCDe9tC3nQcCvdKm9THtIZQhP98jNP1DMJdHxQR6LWgY7b6XZ5m/KgzhA0+qPf -fTrGJGjG/xNpN+MOukNepnytjc0Enf7Ly0djTIIW/Eobk2D+0DppwZ10p7xM+Zu2 -1KxUbVXVA5P+9XY5UdugLm5s0vR6CWL0ZDVDaKYHTj1RzSQw8FikaeAuukverRwl -DKGFfv83T1czCVrxP6MS04rX0+vlPcrjNQzBoB8c/ctqJkEbvhKtsTa8gd4g71F+ -XMsQWumbP/+iyiRYgu9Hw0twL90r36j8qpYhtNFTb31RZRK04zPREmzHfXSffKPy -VxCh/l+z/qkkJ2qbW4zWtjLqxwlDWEJ//+My6i8RJKlZKaiqKpVid5XkRO2SsnQc -YhQZQju9/9kvVDMJOvBLBIlaBx14E71JTkeJ2EHv+UUZ5B9qkaRmpmqrqkryyZKc -kNo72spklQhD6KT//MCpBJOgE78cWerEm+nNsqmcIAxhKX3w0KkEk2ApfiZK6aWY -oRnZVI4QloQr6Zd//+spLA5XooFETcCVaFFLziotLAlX0X+779/qWByuwrnh4FXI -KZd7lNkM4Wr66H/9ZCqT4Gp8JDJ8NdrUlnuVh0mqPlVTVVX70y+wBHQuvfKqq67W -9PoaiNHSyThDWEaP/M8/VDMJluFfx5GodbAMHerIn1CeiDOE5fTgqQenMAmW47PR -8HLcT/fLOeWZeGpGqqaqquaZl1givmz5kNWSxKbDNfTV7z4ZZ3G4BltZHBaXMV+D -eZqXNyotDGEF/dFPDyWZBCvw7eVI1DpYgS51ZaGcbmcIK+mpU4eSTIKV+GE7ErUO -VmKBFmShfNiempWqqaqa83cvyYnaa9TFK1YORXSUMIRV9Lf//vV6JsEqfJGEhlfh -LfQW2YsmoYue+lk43DXEVRf61Jc95RESJVV5KVyjLl7VVZ7dk9VsOqymP37gRJzF -YTWuYXFYgxoSNQGrMaCBXFSWqQlYg320Ty4qDWwmrKGPHPh8nEmwFg/HmQTX4sNR -5VyL/bRf3qjcHVfr4FocoAPyRuVwjCGspe9/4fNxJsE6vLMaiVoH63CQDsoblW/H -GMK19NC7T8aZBOvxdRImwXq8ld4qb1TSLAnr6MvfCzlfjJchYQjr6bEfvjabSbAB -H52ERK2DDfgn9E/kTyp3AkPYQN869dpsJsF1eBCQqHVwHf4p/VP5k8pBSM1J1VRV -LT72nJyoW71m7bXr1MXrNwxl+s1sBlxHv/zzF2NMgo34FGFx2ITXIVHrYCP+Gf0z -+TblCaJOhk1YIrRE5NuUaxnCRvr/3zgVYxJ041cJEnUqdOPthN5O5DuI8iXCZsIm -+o+lH8SYBJvxbsIk2ILfiiFRp8Jm/BShnyLyHUS5i6hTYQseIPQAke8gyjdjLAnd -9L1XviSxOGzF2UjUKbAVDxJ6kMh3EmV5iqZqq6q+UnPyDjlRc93GTd2aXi9BjJYk -Ng0203ff+afFrAa24bv3SkjUJGzDQ4QeIvJdRHnnXik1PVVTVUV++r+XJKo3a3p9 -DcRo6eRCdhlsoT/8cIQCCbbjVwiTYAeWCBJ1CmzHuwm9m8i3KX9L1MmwAw8TepjI -tyl7GMJWeteRl2Kh+KcJEpaEbfSFn70YY3HYhBuQsCRsp8fff6eJxWEnqkjUqbAT -7yH0HiIfIcrD1an6VG1V1XfJ84flBGzZum3jpu2aXi9BjJams2mwg973zecmsRrY -hf/5SgyJOgV24b2E3kvk+4gSpKanaquqTkw9erucqN5RVnw8xqbBTvrsPU9Gis8/ -G0OSmp6qrar6+pyXSnKiemeZuqOETYNd9OF/fCxy8Z2nIsmaqir5mZNyonrX0B5T -Imwa7KavPHUsknznjtE2d1favJ4e+NmDk1gN7MIXf0dGSV4/Iomwhz71kw+ASbAL -P1w8yuSeSsEb6HN//T4wCXbhW7NG+b6hUnAvPfBYWfDUaMG9lYI30he+89tI8N8n -jxK8sVJwHz3whRPTmAS78bs1YU7sxvsJvZ/IR4nSxRBuon/5+VDg+iGB6/HPCf3z -SCDaXycd/sX8RHzfTUOMnqxmSUjTX33t7kUsDnuQIVGnwB58gNAHiPxposyNKhR5 -5OSR6kR1WtPra0O9I9UsCTfT7//9fYtYHG7A2UjUKXADfobQzxD5QaLMKCuWTh6v -SVTfPKx4vIYhmPTYm9+bxCTYi9+oDmPZiw8R+hCRjxFlEkPI0H/4fihw45DAjXic -0OORQBQLPHOQJeJmZiiWUpwhWPTVrx+IMQn24QcEiToV9uHDhD5M5M8S5beEIWTp -gfeOxpgEN+FPJSTqFLgJHyH0ESIvU74qMQROX3vo0RiTII0/iYUm0niC0BNEfpQo -j5FoDcee/Z2SqLayvMwjrit964vf/B+fe/X9mfUzgWDpX0sxWiq9qcmlm5XPkebH -SCepnwMxLJUOf+fbv5xDS6Vffefbv5wjl0oJ5XHS/ASpnw3VWDp54IVw9PH7XwhH -ZeVJ0vx5Uj8d4lg6+dy7H5yeR0ulv3n3g9PzlKdI/WyQsFQq/f706dOnTxNaenzo -o/IXxKgyJL9gWtyQMoMB940pZi4nrLTIfIJbgW9Ilii6gVFn5nLCSkeSUxy36PNh -kbroq18wLW7M9Ypu4OS5Xih4okfbHphe0LVl5xZP9Dg5bvTovSLP9V5P5MWtfL+p -20Iv7Lf1vMjqtsiZrq0Jz9YDIXJWr+m4K/qatWatpckWLZrRorVqOcctDjSZ+Wx7 -m+57ln6Gu+ivZgtjs+0EvcWMZom8bntmj+maemHQE74lCrzJFrrjBtxzzVxZJct7 -zGIu6BK5HLcC4Y0BnhkNfOv167foIuC5JmtISe/jblZ4+sR8l5myhXH1x4DWFNUq -FMtUDkNt0Dzu88DYfRERDod0IbDCuTfaP2YmNEXdzn3fEW5ZQ+spupZh7LyIhPuR -A80WxiWZYo8jtG7ev42bWe5td27lxk2jfV1oVmaKPY6I/mq2MBZXcNCVc6z960TR -57rV22QLveCJoAKQsev8Aj+7Xd3jZpZ75wKRc6z9vaLo8yZb6H2GlnXMnLHnwkGM -sqtbwnU1WxjXVGR5JehR8pqijvzUEKIKM6PFSP8RsA170mxhXDlOhKujTWO1Y+a2 -B54ZcHvQWDpO3bHRnbHOxk+Mad1SdDx+no63OK5tOBWqeR54jiVG72x8oCC8gHv6 -yJwUon1oZKRX6+b9XcNMrrQs7vvdZuD08S6RK+Zd0zOs0fv/2bbREZPDuTnWmW5a -Fvf9tBs6SFtlD5otDLNi/s47Hs2tDKa86a4p02BsG88iHUccQyKaLYzdFwW25XEz -4BuF7Q+D3XqRSO8xrUB4g5otjHW20ESBuwHP8TwPvEHNqagaI2F1DaNZW3Stiq/D -4PaNB9xQYR2X0xHvYfEal46mqKuKTi7LvYYKzMb2c2v73OtzLD7STNieWejVFJWX -86RbZHmDlik6uWyXyBeEy93A6L1oEZ/dv+6KLPc1Wxibzj1LZ1fXFPVazyz0jsbt -//GBR/SFO/D5AA9n0NgwLt2w7fD6HIs3aI7rBGsGAu76jnD9lW52i1PgOcflxt7x -bFfjSs4hrsv/NVsYDeOaHa2b9xubz73cRMBzlshpilrRFfo8KBa6hNvj2EXPDBzh -Dqegb+y7aIGVfZ/ZMrafO7Syzpl4txXd8ep18/4ukc+bbrbcEly8eSpD062y/XCe -RsqJX+hpadUtkfFMTVHLIBo0PsCtYsCNjRNZ2me1WulY/XjHayLHXUbDuGWNS/Km -42pe0V3vBtwzrcDp48by8SC38tnRjUHedMKuDsIPXtE11oyHg4+wlBZBL/d8zRbG -pNBg3nRcY3L5TBl9S4+2f6HN+vAR0hOWZgtjgS20YoZ74Un0VrOg32oWLOFxzeX9 -XaLoBtzzjU2jUZytkRnZGc5mT/fNfCEX1f6mj/LZzfu3m/lCjnu7naB3cyFwhOsb -a8+9TsqbjD5Snd0hM9mNwra5V1439sSiOEfFH+Oywnl4Hp+Y9ljAuz2z0CU8Xj4e -jivLPoJ/EbGo2cKQR82NFo2FzYpZKOQGjUWjZkZT1IjCBq1yRlZPjMkz8iEnbJt7 -mi2MFRdKk7F0XEWlYnJc3r8x9G+0TlS1m/cbHbYQdo5rFZc3BU8EIlPsGblj6XFy -PMt9S3N5/zazf62T40Z2QoSN34ee5b6VdlwniI5yE4EXNhDci/4bN49nIxtOsQkg -DPuqCaELBgtcKzeo0f8/CrpgsMD1TNHJhXXvRisnilmtHFp4kyb0nLBtx7V1s+D0 -GUPfChmtx8nxdER2uiyT7jOGP1phH5IOUyPtuE5g7J/AgpkAEH3IeeRRK2Q0WxjG -REIJc8cwkuW6oGXFetcJWoyp5bvG8g9Go+UNFgKhDyxpXqoX9jsDmqJ2m3neoK11 -crm1nshvW929nd9S5K7FxzbPF1q2RnuPINjCmFMxohVMz+dd3AucHscyA27w0bxf -VBym50c72RkgtowGoVVcz/qu6O/Jmft5NqPbYvib5nEz27Wyi3uBf76XgR9hWheW -XxjT2X0EjDAbWo15laQqahf3gi1C5Bq0ldlsF/fOcqq7mLxa3AvSBSFymi2M5fs7 -fc0Rullw8qbV67jcG9QL+219KD8VdbvVy/O8QVuZzV7nin53x2CB73aC3m4zz40d -ozPxHK3Lx7jSfauX57lmC6PjY0Q/ApVvNI740Htyot8SbuCJnN7XkuGB2aKZFTH4 -xs7R6Tsu8GczrHvcdvyAR3fcIyDOzenwLcHKbHaHiHg2bhy9psYF6qyOyoymwxoR -YWsoz7eVc7gbNNlC31/McM/lAfeHJiDM0mZjz8SBnMPqGQxV3mHbYujeOlPsKV9g -byuzuWOwwM/7WcRYu3rBEwXuBU50o7GozIVZcHSzGAjfMnOOa+t9RpQs58/DR1jU -be5yzwx4tlxKlpfrYcXTqrCqnb3hUdS1Tq58xbCaWznf6K7Y+iJTuu0KP3CsprzI -8pyviwJ3zYLT16r1ODmeHv6a3lzg7sot6/taK+vomP38HLk3ftf6sK9y2C3j1w2D -1ZqNoEKFu31isOCJgUHdFk3lJd5UyJkuj8b0sPfIm4HVyz19KPZwKB0MFni6PJTu -a037gRkU/bQlsjztuIViUMnHbeeXeqGniQDUx6AoM9V1QWGHCdxhdEapce4s8wPP -r7y4tD3Rb+yfSPzjSGU/8HzdDzzHtf100fXNHp62RYvRotnCWDVeC5UwVxYK3M2u -LeZy3WaeG1eNx8bwCcI8U3nVhJQVdY1bzDdoRTdven6vmdvOedZYM54ghgEo6ibu -+6bNR5uZKJRoYzgTyi0Typ2oxdVFH/dywsyOWjXRaHpoNN3XOvI53LGiPnzwj7Ve -Iv+V6IY/l1fK2guNNlwszcZlHrf5QEH3B93AHNAU1RL5gpPjXoPmuH5w8VvvM/zp -ZW+aLYzpZyLpEvmCk+PG1EhBK0uOPbtdaK/ocZsPFMr/NFsMexxCkCoPbSr6wdBv -8yvoL3giz4NeXvTDa7AeXwsPaeN6cjR0BB65zxxjTc9n/cAMT+SVcn35ftPjui36 -8iLv6H1O3liiB4MFHrnX2lva2s/3UfRH2w5Lja/ZwpgbsXK2xLHMgjG/TOtZ8mpo -JhdWcBhtRdHrHHr5gWVYCyfUH36MQT3HbdMaTJefvmq2MBormDf7fd3s95v87P4m -W+hmv69zN1sQjhtEnI5912F8/cK5DOvlt2dCTvWKkjWgB3wg0Llriazj2rpjuqbj -ZvmAljcL0RRPrGcdj209MDM5HmJpGI98NEXNZ/BuWYO6LZo+4Qt35E2hMAzuaY7r -BFrz2EU8PiLPbXxoMwmfXY4DU5afgWnM3fgFYSobH8Kk2cK4ooKmgazdZItyb1Dw -eGFkVg1j6/mVlTE2K2azcr8aIxfN4rShI7ATmBnfcZ1g7C3eBe+15ccFTo9phSff -Yae+1cuzjusEI1dFXtCctsXY1x4uFgjTz6fDl+E03zhIRnvRLNPq5foGHqzyTMf1 -9fVuwHM5Z8P6LDeNZqNNa9GDfEG/Vmw03ayeTqdbbBEdPdPRekhbIp8uvxOSFgHP -pYef86WtfHb0T4VBTxhK89L2jvYOq6N1SXNLNtvRsSSb7WznPNNiZIxstpkbhmVa -LVaHsUAv+p6eczL6QGd7ur2tKee4xYEm2y3qOSdjab7Q2g2lPdvelmnp6MmYVsfS -pby9M2t28Ex26dLOJdlsz9LMEivT2rGEtxvaOcxly7YHOtub2ts0X2iGobQuaeUt -mXYr08x5xmrutHpae3o6Wwxu8k5zSUumjfdk2zvbuFGzty/ri31GYm+fP+hbZi63 -778HAO7Ti4wPOQAADQotLTIzNmNmZjIzMTQ2NDk2ZmQyY2Y4YTYzNjUwZDYyZTIy -NjVlMDA4ZmJmZmQxMzliZDA4NTM2OTNhYTUyNA0KQ29udGVudC1EaXNwb3NpdGlv -bjogZm9ybS1kYXRhOyBuYW1lPSJzYW1wbGVfdHlwZV9jb25maWciOyBmaWxlbmFt -ZT0ic2FtcGxlX3R5cGVfY29uZmlnLmpzb24iDQpDb250ZW50LVR5cGU6IGFwcGxp -Y2F0aW9uL29jdGV0LXN0cmVhbQ0KDQp7ImFsbG9jX29iamVjdHMiOnsidW5pdHMi -OiJvYmplY3RzIn0sImFsbG9jX3NwYWNlIjp7InVuaXRzIjoiYnl0ZXMifSwiaW51 -c2Vfb2JqZWN0cyI6eyJ1bml0cyI6Im9iamVjdHMiLCJhZ2dyZWdhdGlvbiI6ImF2 -ZXJhZ2UifSwiaW51c2Vfc3BhY2UiOnsidW5pdHMiOiJieXRlcyIsImFnZ3JlZ2F0 -aW9uIjoiYXZlcmFnZSJ9fQ0KLS0yMzZjZmYyMzE0NjQ5NmZkMmNmOGE2MzY1MGQ2 -MmUyMjY1ZTAwOGZiZmZkMTM5YmQwODUzNjkzYWE1MjQtLQ0K -******************* -HEADERS: -Content-Length: 2033 -Content-Type: multipart/form-data; boundary=2c8c14eab8a03029c7b116d1756f25d425aa0adf5965a50c439e0cc8e04b -Accept-Encoding: gzip -User-Agent: Go-http-client/1.1 -BODY: -LS0yYzhjMTRlYWI4YTAzMDI5YzdiMTE2ZDE3NTZmMjVkNDI1YWEwYWRmNTk2NWE1 -MGM0MzllMGNjOGUwNGINCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsg -bmFtZT0icHJvZmlsZSI7IGZpbGVuYW1lPSJwcm9maWxlLnBwcm9mIg0KQ29udGVu -dC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0KH4sIAAAAAAAE/7RV -bWwcRxnmbN95fGf7Xt/5Y3x2ku2macdXvHs3tu/sf6hUCokoCqWoiKpcZnfn9g7v -7hy7c27cX4tIBKVISHy0UIhUqaoQIlEbkEBCVRRFEW1Q+ZCoIqhQGlUCUwUiGlRQ -aFHR7PnqEIkfVev7c3vvM+/7PM/MM3sf+8aPv3Xx7ZOv3JjJDqEUDGSH0CAMHfnS -N595VXw2eT4an35tSM+jFL566viArlb5kCpnUQrSOK1lCNPzaAD/4QcJOAD1BByA -YTysZYiuAxrET7x4fEBPo0H42SCkyjk0CAgjLUPODOqAhvAbj14e1NNoCN7IgIKH -YASPaFlyJaPPoDT+8pPfT+tplIYXMnoaZeCnqWRIGnI4p2XJLzLlHMrAKB7VxsiJ -lA4og//2bNIyDP/urR2GcTyujZHrCh7GW6cTGMFWD0aQx3ltjFxJFQBSoBxnh1MD -g0PpzLA+gxD++b+eUAZG4CWkp1EWzqFEwggABm2CvIjKOZSFAi5oE+RZpAMawc+9 -lrTk4J89Uzko4qI2QX6d0QFl8Y3rCTwKJ3qjRmEST2oT5IuqO4dvXHtcEY7BqZ7C -MZjCU9o0OakMjOJrVx5V8Di8MJwIGYcZPKNh8viwDmgMX7iUwHl4usedh1k8q2Fy -Oa0DGscv/ySBAf7eGw5QwiUNk0tqeB5vnUrgCbjUgydgDs9pmDytYMDPX03gApzu -KS/APJ7XMPmaUj6Bf/fWV5S0Ivwpm0grwh68R9tLLmZ1QAX8+ndPKngS/tOLwiTs -w/s0jVxWUSjiM/9IuqfgYq97Cm7Dt2l7ydWRwlT/ZEbRSDY3Ojaeh4lCUZ9Gk/jN -Vx9TyZyGu/Q0moFTanQWTYOOdW0/IeUcmoHb8e3aAfIjRTOFv/3yY0oFht/3LGK4 -A9+hHSAvKYvT+PTWtsXzCFKFfJ84Mzk1rSgBzeDf/nl7yfMIVBPG124k0ovwehZS -hbF+09AMLhT1PJrF3/tVckVmQU+uyCzcie/U9hOsAyrhX15OFJXgSSU+h0pAMNEO -kO8owXP4h33Bf0zBzSGdLc1Nq/mA5vH1NxNJc/BMb+/mYAEvaJj8ZUQHtAff6Jv6 -jTI1DgMQv3JhJJue3zNRKJamUAriK/EAjuOrhhYfJeXKXaupUgkNQBx/9fzZrXkc -x389f3ZrXovjLPlwZbE0iwYhPnf8wtmteRw/9fULCaYRo2KWJtEQxOeeu/72O/tw -HJ9R36RSKqE0xHH81jvqk8LxU/1HUqUfosMR8zsej2jaFt1A0kG706W5gAUi4rYI -nIjuCbuBbPvcIOW2ZNb9zPL4/ZsdvmA024FDj5ot4XOzFQpfPMLXmekKs7Pumr5w -TFd4LHANEbqmFMKzW6wdfGSjYlSM6qIrqgatGkuG1w66xxaZ79SWzSi0zbAbyLbP -zXaT2dxwBc2H3UC2fW64XLYls2gh7AaJIhZFPJSH6CFK9wZcmi0pO8Yn+MP38S90 -eSQfaMvWR0Ug+TFJbbMlPlCZfT4z7JEZrqDFfvEmEfSg25atrmXYwjfdkDVZwMzO -ZigiW3T4oivMbieSIWe+GXJfSG6Q8n3cF5IvGN2OJ5hzJBTNtsepY7aEz81WKHzx -CF9n5ic/c+iIKST3Fm3hedyWIjQ3eOCI0HyPnOY2tSvoPe+x9Sa5EWvyTyeS38+U -Fgscjx8WVkQL4Xb0Qs6cDRa2A0nZbuUt2vQls9QWjPZpI8k7NN//1bE3mNfltLhT -cJhkveK+fpGUI7neDJnPFwyXy09JZq/fyzr0Aw/gNqHZp1PKZ7aLRmSzIBHxsAjX -eUit3do137V9Fq4r8ombySPlm073S2pNKIQ0mt3ArlK4tb6zy659T8jaAZ3vL3Ht -u917Wbj+QOIkGUDp53bRj/Ly7hlHm5Hkfs/Nrh0hi/wG853ashHtbNn/+t6pk7LX -TPQsGB3R2b2T3WZRu/HuPZThpstls+t5u8fru7YKreLF/QyQsmur819QCg5yuRP0 -m3ejG7V2ktXpSu535OaOeHUnhPV5bkt6InVrfgyb2S1uHubybhW/yDwUSO557cOH -HM5ohS4bVVP6HfOg+DgLHLPRaFRd0bC6bc9p9N6YDVv4DZ/LsG2LhpDca9j9t3LD -9p1bS+ovgJLKWq1eq9v1pZVK1XHq9RXHWa1xblWpRR2nwim1mV2163S/2Y1C02tb -5rHVWqO2vOi1g+6xRTfoml7bso1IGDVKak5t2arWmxaz62trvLbqsDq3nLW11RXH -aa5ZK7a1VF/hNWqY3ej/jXO2Zx9brS2qSAqDUrK0ssSrVs22KpxbdmXVbi41m6tV -yhlfZStVa5k3ndrqMqeZBzecSDxEsw9uRJuRzTzvof8GAAD//4LTIC7vDAAADQot -LTJjOGMxNGVhYjhhMDMwMjljN2IxMTZkMTc1NmYyNWQ0MjVhYTBhZGY1OTY1YTUw -YzQzOWUwY2M4ZTA0Yi0tDQo= -******************* -HEADERS: -User-Agent: Go-http-client/1.1 -Content-Length: 7790 -Content-Type: multipart/form-data; boundary=d3406129592c3cc7c861ce3723a1814b640cff3f4da0c26d316b3b224a95 -Accept-Encoding: gzip -BODY: -LS1kMzQwNjEyOTU5MmMzY2M3Yzg2MWNlMzcyM2ExODE0YjY0MGNmZjNmNGRhMGMy -NmQzMTZiM2IyMjRhOTUNCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsg -bmFtZT0icHJvZmlsZSI7IGZpbGVuYW1lPSJwcm9maWxlLnBwcm9mIg0KQ29udGVu -dC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0KH4sIAAAJbogE/7x7 -eXgc1ZWvbqtLOm4h6biJ7Wt5oXzBUCpLVVLJlm0Wb7KNbWxhbIMBA+1S9e1S2dV1 -m6pqLcy8SUNsVgcYEmKzhSUEiIGEbBCyGkICeclzMm+GJCRkEpLMy0cWBpjMC2Tj -fV9Vt9SWFyQb3j9Wd99zz/md3zn33HNvlS9NAsHE9lJJTiWhFpOpJEiYSCWhLvpc -jwk2FQh9cfe3a1kSCC5jEiTwO0kkagoIAgV5krJUbYAEpmhKblC+lGTTIEH/9tsX -apkEtXiXxCRI4jcIErUBavEkepLcoNwhqQ2QxEbaKDcpNxGGUEv3/vsdEpNAwmtr -I1kJm2mzjMrLCYaQpLu+s09iEtThU3VI1Aaow8l0spxWPlmXnpqug/27WU1NKkkS -tcmWOkjQUinJECT62eu/nGAS1ON3JSRqA9TjyfRk+QPK1ySGUEe/+IOrNSYB4D8T -JGoDAE6hU+SpyocIa4Z6uud/Xq2xJEzCQSRqCibhNDpNnqoMMASgjzzySi2TIIU/ -RiRqA6SQUipPV36EDGESffChn81kEjTgvxEkagM0YAttkWconyEMIUV/cNujwCQ4 -Ce8jSNQGOAln0pnyLOVWwhAa6EOvPApMgkb8bi0StQEacTadLZ+ifKeWIZxEX/k/ -DwOToAlfIUjUBmhCmcryHOVawpqhkX7hyYeBJaEZN0eBakZGmTxH2cSaoYne8of9 -wJKA2IJETQHiqfRU+TSFMoRm+uFrflnLJJiMf54a8TUZ59K58nTljakMAel/vfOL -WiZBGl9qRqI2QBpPp6fL05Vnm9KnpOvWX3eXXFOTapLq6mFSqgFOaoSmZmyRIEFL -3QxhMt373D6JSXAy3lEXqT8Zz6BnyGnlQ3WsGdL09afvllgSPoDTI2gfQIUqcqsy -nSGcTD+y/2GJSTAF/4UgURtgCqpUlecpzxPWDB+gd939sMSSMBUDJGoKpmIbbZPn -KZsYwhT64s+iudPwG/HcadhO22VN+RpJT08D2XfNOrLvmnUpaXL65A9MKSfQMjYN -ptJfVzL5ztFMTk9J15Eff2FRTU2qdmol4Q4obCpMozdcv6udJYHiYibBdLwrEXlC -Uae63KHoagNMx07aKRvKnQnWDJS+8OC/t7MktOBmJGoKWrCLdsnzlTMYwnT66Lde -bmcSzMCfECRqA8zABXSB3K28RBhCC73mhZfbmQQz8b8JErUBZuJCulDuVn5HGMIM -+sBz0exZ+OuYsVm4iC6Su5UfEoYwk+7//svtTILZ+GACidoAs3ExXSx3K1cnGMIs -uvuZl9uZBKfgMwSJ2gCn4Jn0TLlbOUBYM8ym//X8r9pZEmTsQqKmQMaz6Fny2YrK -muEUuueZX7ezJMwpD87Bc+g58hJFYc0g0x/+MBpkODvymOFSulRepsxkzTCH7tn1 -Tj1Lwqn4j0jUFJyKy+lyeYUyxBAY/eprf69nEpyGu2PEp2EP7ZFXKG8ThnAq/cr9 -v2tjEszFZ2K25uJKulJepXyOMITT6L/e89s2JsHpeG8CidoAp+Nqulo+V7kxwZph -Lv3+919tY0k4A9ciUVNwBq6ha+RzlUUM4XT6m5/8oY1JoOBbsWoF19K18jrlV4Q1 -wxn04Wf+0MaS0IofjFC34nn0PHmdso4hKPT5PX9sYxKo+JWYSBXX0/XyBuVewhBa -6Rsf+2Mbk2Aevh0Pz8Ne2itvUN4irBlU+u3f/LGNJaENT41Ut+H59Hx5Y7Qm5tGf -3vp6G5OgHX9UH6luxwvoBfIm5bd1DKGNfvyB19uYBBr+FJCoDaDhZrpZ3qTcDQyh -nb657/U2JoGOt0A0W8ctdIu8SbkZWDNo9OXrr9VYEjpwTmS5Ay+kF8oXKaewKaDT -B6+7VmNJ6MQ6lgSjXEs6cSvdKl+sSGoKDLyEXiJfpDQxhA5654u3JpgEXXhTAona -AF14Kb1U3qbcStJt6bqamuTDt5+WaplGp7fMmDlr9inyHHbqaXNPP0NpVee1tWt6 -R0sdJGipBGwqdNJP7n47wZIwH1uZBAvwbhIZn4+X0cvkyxVFbYAFeAW9Qr5c2UcY -gkFvf+rVNiZBN+4jSNQG6MYMzcjblb2ENUMX3f3kq20sCQuxLVK0EE1qyn3KPNYM -8+lbT7xWy5KwCE9HoqZgEVrUkvuUuQxhAb3z6tdqmQSL8bE4dosxS7MyVz5FGEI3 -/fgXX21jEpyJ9xIkagOciTmak7crHyUMYSG9+fOvtjEJzsKbSaT7LLSpLW9XBlgz -LKL33BOhOhuXIFFTcDb20355u3IOa4bF9PWnygl3KZL0aem6mhpy3TUdqXSn0TV/ -QffCRYvHMjeTNcOZ9G/PPJBkSTgHZ0Y6z0GHOvIOZQZrhrPo3pfvT7IkLEEnGlyC -O+lO2VW2MYSz6VvXfT3JJFiKN8ZsL8U8zcueMswQzqF/euL+FibBMvzw/Ci8y1BQ -IReUj+kMYQn99P77W5gEy/F2HYnaAMvxSnqlXFBu1xnCUvqjP38kwSRYgf8Wz16B -PvXlbcpT89k0WEb/9Ye3JpgEPfj5+UyCrso66cGABvI25bPz09PT9TU1t036xdVy -qv7Ms84+Z8nSZS0SJOiBWoawnO46eH8tk2Al7o1nrsSQhnJRuZUwhBX0e398uJZJ -sAr/M24tVuEAHZAHlfvqGEIP/eutj9QyCVbjS/EaW41DdEgeVH5czxBW0t/96lGV -SXAuvh0Pn4vDdFi+Svl9PUNYRQ++9qjKJFiD++MluAb/gf6DfJXyKYhR/6/pPyjJ -qfrlK3pWriqjvo8whNX0Lz8uo36cYLRN1dSUSonrS3KqfnVZOgkJigzhXHrLYw/V -MgnW4uMEidoAa/Ef6T/K/yNOxDX0pl+XQf6tHkl6Wrq+pqYkHyjJKencNavKZJUI -Q1hL/+W2gykmwTr8dKxpHf4T/Sf5g8rdhCGso7dfdzDFJDgPP0qQqI1wHpYILRH5 -g8oewprhPPrpv/yhkSVhPRpI1EZYj1cTejWRryFKJ2uG9fSVm19pYEnYgLOi8Q34 -IUI/RORdRJnBEDbQe/77Z01Mgl68iyBRm6AXdxO6m8jXEuUOkm5J19XU1P/8IZaC -tevOW79+w9JlcWk4kGQIvXTPl/5WyyQ4Hz+TjGafj9cReh2RryfK/UmGcD7dffD2 -RibBRnwsiURtgo14A6E3EPlGouxPpqem62pq6va/wFLJ3vMruksSmwIb6U+++UCS -JeEC7GJJOAdnIVFPggvwJkJvIvIOpZMhXEB/9PPrmpkEm/D1JUjUJtiEewjdQ+QP -E+WdboawiR48GElsxr93I1GbYDPeTOjNkcTfu9PT03U1NTO/+IKcqt941tkXbKpg -iPrCzfRP//F0C5NgCz4fs7MFbyH0FiLfSuIAbaEHfxlJXFjh70L8Z0L/OZK4ixyy -UjaedfbmLeXgH6hlU+BC+uPb7k6yJFyEq1gStqKGRG2Ei/A2Qm8j8keIco7aCFvx -o4R+NPrayqbBRfSuXZ9IMgkuxhuTTIJL8I4EErURLsbbCb2dyDuUG5JqI1yCHyP0 -Y0TeodyYYAhb6dsPfSLJJLgUr61FojbCpbiX0L1E3qF8PcEQLqbXvflAkkmwDV8m -SNSTYBvuI3QfkXcoGdYMl9AffuuBJEvCOTgbCUO4lO598aczmASX4T2TkKhNcBne -QegdRL6TKNcCQ9hGXzv40xlMgstxNyBRm+ByvIvQu4h8J1F2Q3pmuq6mZt7eJ+RU -w4UXbb34krPOvnTb0mUtEiRoaTubCpfRT//q+QST4Ap8kLAkZPC8KIJX4N2E3k3k -e4hyP1EbIYMfJ/Tj0fdzGcLl9P++ejDBJNiOT8Yx2Y73Enovke8jyuOETYMr6HdL -308wCUy8gTAJ+vBrCSRqE5h4P6H3E/k+olxP1CbowwcIfYDI9xHlqwnWDBn61kuP -SywJFs6ISLfwE4R+gsgPEmVJmqbra2o+X3fgGjlVd9nlV2QqvkhsMmynb77xg3ms -DrL45oclJGozZPGThH6SyA8R5Y0PS+kp6bqaGvLz/70gVbu9koUHTmOzwaQv/n2U -Bgk4fp4wCXJYir3j+DChD0fuf46ojZDDRwh9hMj3EOUShtBHr9/zQoJJkMOPECSs -GSz63C+fT7AkZHBd9EOW7nv7jXaWBBvViGEbP0Xop4i8nyh31KZb0vU1Nd8kz9wo -p8Dssy6/IlspYVPYZOD05q8+MYnVQT/+50sJJGoj9OOjhD5K5MeIEqanpOtrau5u -uvVqOVXLy+G9L8EmQ44+dtMD8cRnHktEbX59Tc3TM18oyanaXNnErYRNBpve8d17 -Y8lnH0wgibmS9x+QU7V2hasSYZOhn7704N5Y8o1rYslRnf1l2JFOh+765e0x7Of/ -TMZIOqOSCDvogz/7KzAJ+vHv88YI7qgW3Emf+MzbwCTox9emj7G9s1rQpbvuLQse -HCvoVgvm6XPP/ikW/I+TxgjmqwU9uuuhuyczCRz8Zl0UAwcfJ/RxIn+aKD0MQdBH -PhEJ7KgI7MDPEPqZWCA9NV1XUzPpxl/PSSU9UanDB2pZMxTo75+6YS5Lwk5kSNRG -2IlPEPoEkT9LlFnx1kbuOrCnNlVbWLqspR4StHRgTy1rhivp975x81yWBBdnRIhc -/ByhnyPy54kytTyxdGBfXar2ypGJ++oYgk/3/u5bk5gEefxKbTQzj18g9AtE/iJR -JjGEgH77e5GAVxHw8ElCn4wFYl9g/26WSvrBSHYkGUJIf/L0rgSTQOBfCRK1CQQ+ -RehTRP4SUf5EGEKR7nor6pwL+HMJidoIBXya0KeJvE15UmIIA/SnH7snwSS4En+W -QKI2wZX4ZUK/TOSvEOXecmYmHvuzkqoNiwOx7QO45o3rXtz/+4//5O1pLdOAYOkX -pQQtlX6nyaXtyldJx9fIItIyExJYKt347Nd/M5OWSr9/9uu/mSmXSinl66TjG6Rl -BtRi6cCu56LR+255LhqVlQOk4xnSMgWSWDrwxJt/fecUWip99s2/vnOK8ixpmQES -lkqlv7zzzjvvvENo6b7KR+WbxKgxpKBgWtyQ+oZDHhiNpusKKyP6dnArDAzJEkUv -NBpM1xVWJpZsdLxiwEdEGuKvQcG0uDHLEvmCz4NAz7lmyDWPD67k0cfVZhAaeb1f -5Lne74u8uIrvNHVb6IWdtp4XWd0WrunZmvBtPRTCtfpNx1s20KF1aJ3ttujUjE6t -S3MdrzjUbuaz3fP1wLf0Q83pWZ5zzZDnzCDUbGHMGYNGUSs/CL9VczwnNJz/P4g0 -Wxh0DJpePrjVd0LuV7FmX+UUNEWNf2/VtvpOyA3+vmG0r3IKun2VU9BsYcz1i17o -5LleKPgipylqwRc5x+Urio6b5X6r1ld03KyRe69jeIhZveCLUGi2MGYc8rtWBhMz -876DKPgip9nCSDlC28TN7HLXNS5+rx13hO4IzRaGZTthf7FPs0Rez/PQdyyhi5C7 -7ZZwXW6Fwtd9bnFngPt6YdgXgSUK3OcWdwa4rylqYdgXgSUKfFP5t1atPwwLa0wv -63J/rWfzIDTWjV17F1y8duP4zegj9mxhbD0hwB4f3FjxYlPZLy1X9KxOY7bHQ70/ -DAtaGfzqomdpm7k/wNds2bLR6BvrxIkWkIo9PeB+2Tm1yjlb+I7rmnq+OKQp6iZR -DLnfWoVnzVg8RyJ1gHtZ4etHVhuptoWx1haaKHAv5C7P89Af1hyhj8bfEl7Osct/ -IoIUNcsr5U8cAuqwmnYsUBOzOlJwHeFptjCCCc7XFHUz9we43xP506ptESPfQz4U -anlzaBO/ssiDcIXIDm92ruJrvZD7Fi+EIk4Sw+ifCOkT9m+EYFsYFx45KF7oO326 -4wWhX8xzLzRDR3ijqSRC7pZDlHeyWZcPmj5v1aIMi7I4mEjWHNGBcSLQ+00v63Jf -s4Wx+cR86eWDG0aciQLRqXUavUeGV6laMZuHZq3lOtwL13o5Ua5P1akrTpiZo5rW -Y8OOlxNVrMiVqGkB9we4X8ZUBWnqiISiWsLzyoE0Zh26XW4OTT/s2XjhxnjTNM6v -Xu6+mTM9c7R2t9tCd7yQ+57pljebLM+ZRTfsqThwmMLDSt+xVvWEbJc3fFsYZ1fX -qCOA1hTVKhQ3+iLnuHwEaqvm84CHxtaJBO9dEI4E8kRgRRQa3e9iK6pJUUUrz4iy -2zAuHM+GeYTafgTe9CA2oNnCOLmvmHOE1ssHN3Ezy/3NzlXcuGKsrRPd1/qKOUfE -/2q2MOZVBbbHdayda0Qx4LrV326Lcss1Asi4aCKL8N306j43s3EFOhoI17F29oti -wNttoQ8YWtYxXeOS42O/2rkxenVLeJ5mC2NpVZYfQz6uVWVorRGqKDM6jcz7gG3E -kmYL48yj0DoiVGZqZVw0Vjqmuzn0zZDbw8bi4/bukHU2fmJM68qi4/PjNLzR8WzD -qZp6lAaYDxWEH3JfH41J+UgwOtKv9fLBnhGSllsWD4JeM3QGeI9wi3nP9A1rPHk1 -qnIkNw83ppuWxYMg40UGMlbZgmYLw6yKwXH7o3nVzpSL7qoyDcam8SzScfhRERln -W18RPzozmuVzM+TrhR2MgL3gPSI9Z1qh8Ic1WxhrjtzOlLekEZxazwia6Cgx+nWE -ycvHA65S54/YQ401Omo92rzGNUdT1JGD9ijIo3VtFYsB9wcci482E7ZvFvo1Ra34 -3yuyvHxy7xH5gvC4F76HDfSR7eueyPJAs4Wx4Tjhn+ubhf7yjcMI7sDof89CdRTg -MX1RBT5mpI88XVtRdNyssW5cc6O2wx9wLB5fRa0aCrkXOMILlnvZjU6Bu47HjW3v -WXJWIJf/arYwWsflpNbLB43zjy0qQu5awtUUtaorDHhYLMQHvaJvho7wqkL53q26 -su3RVaHZwug+Drybit545/XywR6Rz5tettwSvHdxGnUn1h/FaXRLDwq5zi7dEn2+ -qSlqGUSrxoe4VQy5sX4i+XJErdWGq69CjmJ4VWy4x2g9srooJSKFI7LGyXnT8TS/ -6K31Qu6bVugMcGPJeDLdymfHXlnlTSfq6iD64Bc9Y9UJaMqIsJ/7gWYLY1KkMG86 -nnFS+Y4y/pYZi/REm/WRk6QvLM0Wxqm20Ip93NeEb+tXmQX9KrNgCZ9HHYEoeiH3 -A2PDWBTHOg8eWZ8emPmCG+/97Uez2csHN5v5gsv9rU7Yf34hdIQXGKvHVTr00dss -r6Imu17YNo/vcjoNeyKHrImZrDJui3crYOWqWDXnMMBbfbPQI8qXH8b4smy0bTgk -niJmUbOFIY8Z0+KxqFkxCwV32Jg7JjKaosYUtmrVEVl5AnhcYdvc12xhLJsgyYfR -ZCw+DqLXR/aNrolO7eWDxkJbCNvlWtXzo+gZQl8xN9oW5RyXZ3lgaR4f3GQOrnZc -bmQnRNj4behZHlgZx3PC+Cg3EXhRA8H9+K+xfUKLYwII+4qOG+0s40YXDhd43N68 -r+jC4QIfRXeZ5YpiViu7Zom8bgvdFbbteLZuFpwBo/Kt0KflHJdnYrIzZZnMgDHy -Mb6KzESpkYke/e2cwIKZAJAKwLJFrdCn2cIwJuJKlDuG0VzeF7SsWOs5YafRVL5y -LP9gtFn+cCEU+tCCjsV6YaczpClqr5nnrdpqx3VX+yK/aWXvZn5lkXsWP7x5PtFt -a6z1GIItjJlVI1rB9APew/3QyTmW+b48zqzGYfpBXMkOAbFxLAitulvxxGDONXfy -bJ9ui5Fvms/NbM/yHu6HwfFeBo4oO1S1LqwgesBwSGd3FBhRNnQZp1STqqg93A83 -CuG2asuz2R7uh+9vfC3uh5mCEK5mC2PJzkWB5gjdLDh50+p3PO4PR8/xK/mpqJut -fp7nrdrybPY8Twx6W4YLfKsT9veaeW5sGZuJx2hd3sWUHlj9PM81WxgL30X0KKgC -o23Uhp5zxaAlvNAXrj7Q2cdDs1Mzq3wIjAvH0jwu8EdSrPvcdoKQx3fcoyCOzWm5 -CEdx3yJino3LxtM8l5uRdzFUZjQT7RExttZyvOMnKO220HcW+7jv8ZAHlQBEWdph -XDJxIMfQeghD1dfHtqjcW/cVc+UL7E1lNrcMF/hxP4s4XK9e8EWB+6ET32jMLXNh -FhzdLIYisEzX8Wx9wIiT5fh5OIpG3eYe982QZ8tbyZLyflj1wky0qx254VHU1Y7L -WzXHc8KV3HIDo7eq9MWqdNsTQehY7XmR5W6giwL3zIIz0KXlHJdnRr5mzi9wb/nG -tQNd1fvoYfX8GAti/Kb1EVtltzvHPzdyVuswwqop3BsQwwVfDA3rtmgvL/H2gmt6 -PB7To94jb4ZWP/f1iu/RUCYcLvBMeSgz0JUJQjMsBhlLZHnG8QrFsJqPDx5f6kWW -JgJQPwxFmameE3I7SuCFxqI4NY6dZUHoB9UXl7YvBo2dE/F/HKkchH6gB6HveHaQ -KXqBmeMZW3QanZotjBXj1VANc3mhwL3s6qLr9pp5bpw1Hh0jJwjz0MkrJjRZUVd5 -xXyrVvTyph/0m+5mzrPGqvE4MQJAUTfwIDBtPlbNRKHEheFQKFdOKHfiFlcXA9x3 -hZkds2ri0UxlNDPQNfo5qlhxHz78fq2X2H41upHP5ZWy+kS9jRZLhzHb5zYfKujB -sBeaQ+X3DB2XR28ZBu9Da3aIveilHCdaRsaUQ5H0iHzBcbnRFE/QypKHnyxP9Czg -c5sPFcp/NFuMWKwgSJeHNhSDsPLbnCr6C77I87CfF4PoGiwXaNEhbVxPjsqNzbG0 -6flsEJrRibz63nMgP2j6XLfFQF7kHX3AyRsL9HC4wGPzWnfn/O7jfRR9dN3RVhNo -tjBmxawcKXEss2DMKdN6hLyqRPK0Kq/jUhS/zqGXH1hGe+GE+sN3Uai73Dat4Uwh -fgtEs4XRVnX1aw4GujkYtAfZne220M3BQOdetiAcL4w5Pfxdh/H1C8dSrJffnok4 -1au2rCE95EOhzj1LZB3P1h3TMx0vy4e0vFmIQzyxnnU8uvXQ7HN5hKV1PPJxiDoO -4d2yhnVbtO8IhDf6plDkBvc1x3NCrePwRTw+Io+tvFJMomeX48CU5YdgOuxu/IQw -lZVXMGm2MM6oomkoa7fbotwbFHxeGI2qYVxwfNvKYTqrolldrw6Ti6M4uXIEdkKz -L3A8Jzz8Fu+Ea235cYGTM63o5DtiNLD6edbxnHD0qsgPOzK2OPy1h/cKhBnkM9H7 -+Fpg7CZjrWiWafVzfR0PV/im4wX6Wi/kruusW5vlptFhzNc69TBf0M8V600vq2cy -mU5bxEfPTLweMpbIZ8ovRWdEyN3MyHO+jJXPjv2pMOwLQ+lY3L2we6G1sGtBR2c2 -u3Dhgmx2UTfnfZ1Gn5HNdnDDsEyr01ponKoXA193nT59aFF3pnt+e/wfDGyvqLtO -n6UFQus2lO5s9/y+zoW5PtNauHgx716UNRfyvuzixYsWZLO5xX0LrL6uhQt4t6Ed -Q1223XW84lD70KLu9u75WiA0w1C6FnTxzr5uq6+D8z6rY5GV68rlFnUa3OSLzAWd -ffN5Ltu9aD436rYNZANxuZHaNhAMB5bpupf/vwEA7MYm8EZEAAANCi0tZDM0MDYx -Mjk1OTJjM2NjN2M4NjFjZTM3MjNhMTgxNGI2NDBjZmYzZjRkYTBjMjZkMzE2YjNi -MjI0YTk1DQpDb250ZW50LURpc3Bvc2l0aW9uOiBmb3JtLWRhdGE7IG5hbWU9InNh -bXBsZV90eXBlX2NvbmZpZyI7IGZpbGVuYW1lPSJzYW1wbGVfdHlwZV9jb25maWcu -anNvbiINCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtDQoN -CnsiYWxsb2Nfb2JqZWN0cyI6eyJ1bml0cyI6Im9iamVjdHMifSwiYWxsb2Nfc3Bh -Y2UiOnsidW5pdHMiOiJieXRlcyJ9LCJpbnVzZV9vYmplY3RzIjp7InVuaXRzIjoi -b2JqZWN0cyIsImFnZ3JlZ2F0aW9uIjoiYXZlcmFnZSJ9LCJpbnVzZV9zcGFjZSI6 -eyJ1bml0cyI6ImJ5dGVzIiwiYWdncmVnYXRpb24iOiJhdmVyYWdlIn19DQotLWQz -NDA2MTI5NTkyYzNjYzdjODYxY2UzNzIzYTE4MTRiNjQwY2ZmM2Y0ZGEwYzI2ZDMx -NmIzYjIyNGE5NS0tDQo= -******************* -HEADERS: -User-Agent: Go-http-client/1.1 -Content-Length: 7294 -Content-Type: multipart/form-data; boundary=ca9269333d6808c1ca8fa1aba3c2c8f2eb7d6e0a61a67df07779cabd3c1b -Accept-Encoding: gzip -BODY: -LS1jYTkyNjkzMzNkNjgwOGMxY2E4ZmExYWJhM2MyYzhmMmViN2Q2ZTBhNjFhNjdk -ZjA3Nzc5Y2FiZDNjMWINCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsg -bmFtZT0icHJvZmlsZSI7IGZpbGVuYW1lPSJwcm9maWxlLnBwcm9mIg0KQ29udGVu -dC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0KH4sIAAAJbogE/7x6 -e3QcxZmvajQtfR4h6fOA7bJs43aBodWWuqWWLck2GNvyG78fGGPscaun1Go00zV0 -98hWdvdmYG0exiHe8DAEEzA4ODGPkHBDNifZrElI4JzNdbKPvMlmN2EPhyVhgWUv -kBC453SPpJFkhGSb+4803fU9ft+jvvqqqq+LA8HYnkJBTsShHOOJOEgYS8ShIvxd -iTFWC4S+/NQRicWB4HIkagIIAgV5gjKdIcToKz++X2ISxPCfYkjUKohhgibkKuXe -GEMop8+/eJ/EJCjHxyuQqFVQjhfQC+Rq5YsVyclJWHzwYXnxwYflRDmJlddJEKOF -xWwKxOk7z3xRYhLE8X7CJJDwB3EkahXEsYbWyLXK3UStAgmRolyr3BxnCBI9flvI -UoF3lCNRq6ACJ9KJcq1yOsZqoYL++V8elVgcKnFPaEQlJmlSvlChrBYq6Qe/fkxi -cQDcFA4CXkQvkicpbQwB6L++fUJiEkzAr8ZCwRNwMp0sT1GOxRjCBLr/zRMSkyCB -D0bDCaSUylOU9wlDSNC7fndCYhJU4TsEiVoFVTiVTpWnKP9NGEIVffbnJyQmwQX4 -eRIKvwDraJ08RbmbMIQL6FO/OSExCarxbyPuapxGp8lTlGdJclqy8ouxwq1yWVli -QlyqqIQJiaoL6iSI0UINQ6imR54PXV+D91cgUaugBqfT6XK18tcVrBZq6BvfPCqx -ONTiVCRqAmpxBp0hX6xMZQi19K6TJyQmAeI/EiRqFSDOpDNlWXmBsFpA+sDRExKL -w0T0kagJmIiz6CxZVjYzhIn0J78+ITEJkvj3BIlaBUlklMlTlL8jySnJirIyct/N -axJSdU0tTqyrgBgtFBazyZCkt9+2v5HF4UKczyS4CB+IIVETcCFeQi+RL1V0tQou -wtl0tnyZ8vkYq4UL6YvH/7WRxWESbgktmISX08tlRbmcIVxEH//+S41Mgsn4y8hz -k7Ge1suq8gvCECbRm198qZFJMAX/JxqegnPoHFlVXiMMYTJ95PmXGpkEFF+Ohik2 -0AZZVX5KGMIUevJHLzUyCabi8SjmU7GRNsqqclOMIVB64LmXGpkEdfhcxF2HGtVk -VTlFWC1Mpf/9wu8aWRymYUuIexrqVJebFJXVQh099NzLjSwO04uD07GZNsuGorBa -mEZ/+tNwcAZejERNwAxsoS3yXGU6q4Xp9ND+DytZHC7Gv0SiJuBinEfnya3KPoYw -g3779Q8qmQQz8UCEeCa20Ta5VXmPMISL6beOvdbAJJD7EcvYTtvl+crXCEOYSf/5 -wf9sYBLMwodiSNQqmIUL6AJ5oXIwxmpBpj/60asNLA4MVyNRE8DwCnqFvFBpZwiz -6Cu//EMDk+ASfDdyxiV4Jb1SXqT8jrBaYPTEc39oYHG4FD8dmnQpXkWvkhcpaxjC -JfSFQ283MAlm47dImMKzcTFdLC9RHiIM4VL65r1vNzAJLsP3ouHLcCldKi9R3iWs -FmbTH7zydgOLw+V4SSj6cuygHfKyMMMvo786/EYDk0DBn1WGohVcTpfLK5T/rGAI -l9MvPPJGA5OgHn8FSNQqqMeVdKW8QjkKDEGhb933RgOTQMXPQsit4iq6Sl6h3Ams -FurpS7fdorE4zMFZoeY5uJqultcoM9kkUOnxW2/RWBwasILFoRHrQpIGvJpeLa9V -JDUBjbiOrpPXKDUMYQ79/E8Ox5gEGt4RQ6JWgYbr6Xp5g3KYJBuSFWVl8RP3XJqo -S1540aTJU+jUumnTZ1w8U57FLrl09mWXK/XqnLoKiNFCAdhkaKBfPPBejMVBx3om -QRMeJUjUBOi4kW6UNymKWgVNuJluljcp9xGG0Ejv+carDUyCZryPIFGroBm30C3y -VuUIYbWg0QPPvtrA4mBgAxI1AQZuo9vka5Q5rBZ0+u7Tr5ezOLTgZUjUBLTgdrpd -vkaZzRCa6Odver2cSTAXnyBI1CqYi9fSa+UdypcJQ2imX/j6qw1Mgnn4EEGiVsE8 -vI5eJ29V7iYMwaB3PvNqA5OgFe+MLGjFnXSnvFXpZbXQQh988NUGFoc2XIRETUAb -Xk+vl7cqV7JamEvf+EYx4a5Dkrw0WVFWRm69uSmRbGjU9KZmo2XucM9NZ7Uwj/75 -uUfiLA7tOD2U2Y676C55tzKN1UIrPfLSsTiLw3x0wsH5mKIpeY+ykyG00Xdv/U6c -SbAAD0beXoAmNeVOpY8htNN3nj5WxyRYiJ+ZG4Z3IVrUktPKvTpDmE+fOnmsjklw -Bd6jI1Gr4ArklMtp5R6dISygP/vjXTEmwZX4LxH3ldhFu+QNyjfmsimwkP7zTw/H -mASL8Jm5TAKtf54sQpva8gblq3OTU5OVZWWfm/BvN8mJynmtbe3zFyyskyBGT5Uz -hCvo/tPHypkEV+GRiPMq7KbdsqMcJgzhSvrDt0+UMwkW439FS8xivIHeIPcoD1cw -hEX0/cNfKmcSLMFfRHNsCWZoRu5Rfl7JEK6ir/3ucZVJsBTfi4aXYpZmZVf5fSVD -WExPv/64yiTowJPRFOxAQYXsKl+GCPX/mfrjgpyovOLKRVctLqJ+mDCEJfRPPy+i -fpIgSU5NQllZoRC7rSAnKpcUqeMQo8gQltLPPvFYOZNgGT5JkKhVsAxzNCffqHyZ -MIQOesfLRZB/rkSSnJKsLCsryKcKckJa2rG46KwCYQjL6D9+7nSCSbAcn4okLUeP -erKvHCUMYTm959bTCSbBCrw7qlYrMKCB7CuHCKuFFfSpP/2hmsVhJRpI1ASsxDzN -y71KM6uFlfTf7/z3KhaHVTgjHFyFe+leeZ8yjSGsog/+z69rmASr8YFI8Grso33y -p5T7SbIuWVFWVvmbx1gCli1fsXLlqgULo3pwKs4QVtNDf/vncibBGvxKHIlaBWvw -L+hfyH+pHIszhDX0wOl7qpkEV+MT0fDV+Ff0r+T/pZyMJycnK8rKKk6+yBLx1Wv6 -pRYkNgmupr/83iNxFoe12MLi0F7EvBY/TT8t71aaGcJa+rPf3FrLJFiHbyxCotbA -OiwQWiDyTUT5sJUhrKOnT4cU6/GD1pBiPd5M6M1EvokoH7QmpyYrysqmf/1FOVF5 -dWvb2nULFkZ17jBhCOvpO//xzTomwQZ8gYTMG/CvCf1rIu8nUTQ20NO/DSk2Rk6r -gY14gNADRN5PlAdIlGDFaXF1a9v6DcVInypnk2Aj/fnnjsZZHDbhchaHzaghUath -E95C6C1EvpUoV6rVsBlvI/S28LGeTYFN9IH9j8aZBFvwYJxJsBXvjyFRq2EL3k7o -7UTerdweV6thKx4k9CCRdysHYwxhM33vsUfjTIJteEs5ErUatuEdhN5B5N3Kd2IM -YQu99a1H4kyCa/AlgkS9AK7BQ4QeIvJuJcVqYSv96fcfibM4tOPFSBjCNnrkJ7+a -xiTYjg9OQKLWwHb8DKGfIfKdRLkFGMI19PXTv5rGJLgWDwAStQauxc8S+lki30mU -A5CcnqwoK5tz5Gk5UbVx0+YtW1vbtl1T9E9hD5sM2+lTv3shxiTYgccJi8N1eDUS -tQZ24GFCDxP5b4hyjKjVcB1+jtDPhc8rGcK19P++ejrGJNiJz0Yx2Yl3EXoXke8m -ypOETYEd9B8KP4oxCa7H2wmTYBf+XQyJWgPX4z2E3kPku4lyG1FrYBfeS+i9RL6b -KN+OsVq4jr77iyclFofdOC10+m48QugRIt9HlEVJmqwsK3um4tTNcqJi+7U7ruu3 -RWITYSd9680fz2EVkMK3PiMhUWshhfcTej+RP0+UNz8jJSclK8rKyG/+aV6ifGd/ -Fp66lF0M19OffDDoBgn24DOESWBiIbJuDz5A6ANE/huifI2o1WDiUUKPhs87GMIu -etuhF2Mhw10ECauF3fT5374QY3G4DtcgYbWQove992Yji0MnqkjUGujEBwl9kMhf -IMr95cm6ZGVZ2ffIcwflBFy/a/e1O1LFLC5MYhNhD73z209PYBVg4X/9IoZErQYL -HyL0ISI/TJQgOSlZWVZ2tObwTXKifE8xvA/H2EQw6RN3PBIxPvdEDElE+c3pLxbk -RLm5YGGdBDF6mLCJ0Env/4eHIsrvHo8oK8rK5JOn5ER5Z7+vCoRNBIv+4viRiPLN -m4fLtIraQ5lpuv+390xgFWDhC38kwyjTg5QInB7/9fvAJLDwgznDRPJSwi769Ffe -AyaBha9PHaa7q5TQpvsfKhKeHk5olxJ20+e/+05E+B8XDCPsLiV06P7Hjk5kEqTx -exVhDNJ4jNBjRH6EKB0M4Qb6pUdDAt5PwPFRQh+NCKIqPOHgy7MSceeG/hp4qpzV -Qg/9/Tdun83i0IUMiVoNXXic0ONE/iJRZkTrGHng1KHyRHnPgoV1lRCjhVOHylkt -ZOgP//7O2SwONk4LEdn4GKGPEfkEUSYXGQun7qtIlGcGGO+rYAhZeuS1709gEnTj -t8pDzm78EqFfIvKXiTKBIbj0Bz8MCZx+AgdPEnoyIohsgZMHWCKedQeyI84QBP3l -N/fHmAQ34PsEiVoDN+DjhD5O5CeI8g5hCDm6/92wTe7B30hI1GrowScJfZLIG5Rn -JYZwI/3VvQ/GmAQZ/HUMiVoDGXyK0KeI/BWiPESSk5IVZWWxJ/6oJMpF7sZI9ylc -9fSP7nzg+EO/fG9K3RQgWPi3QowWCq9pcmGP8jRp+ippJ3XTIYaFwsHvfueV6bRQ -+P13v/PKdLlQSChfI03PkLppUI6FU/ufD0cf/uzz4ais/G/S9HVSNwniWDj19Fvv -fziTFgpffev9D2cqz5K6aSBhoVD404cffvjhh4QWHu7/qXyDGGWG5OdMixtSZ1/A -faPazGSElRKdN3Ar8A3JEnk3MKrMTEZYqYiy2nHzPh8gqYoe/ZxpcWOml3cDJ8v1 -XM4TXZqi5jzRtc7M1WsZIXryOSOtd4ss17s9kRWf4j2mbgs912PrWZHWbZExXVsT -nq0HQmSsbtNxF/c2aU1ac6MtmjWjWWvRMo6b39doZtOtc3Xfs/QhCvWsmdNsYahD -3hZhOBm+NO9k0tyr18x0umPjtmVmYBpdnyyinCcCodnCmDbUNUVE2z0n4N4nDiKM -hi2Ma20n6M53apbI6rZndpmuqef6POFbIscbbaHbIs0zgZnzRJfuuAH3XDMzJJil -XvQDz3Ht1W6a7zPc4W7cdO3qjboIeKbREpkMtwLh6b3cTQtPP3sQg+7ccV4t4Vkn -WCssM3CEe55FrxWWv0J4WwLT6jF2n5PoZTwTmKu4mdvoiS4nw716Lcyf4qtAGP7/ -nyiEOZLq5tFs6xhHKDRFLaItGlA0xdg9fBqep/zpB7ngY0BqirqF+74j3Hotnc/m -StxsbDuPXvUjJZotjHnjgJTPZYSZDgtW0zgs8bjPA6N9HByB2cO3uGbO7xaBb7SO -g3NLYHqB1pV3rWZjxtBiFw51bNzWH+wNHwNoeOlJ8y4znwk6+gvJCIGd5zHvh+oe -rJ1XfAxoTVGtXL6Y2QNQ+2Ow/TwiHCio5wIrdOFZBtgwLuzMdzlCW8/3buZmmntb -nE+dYQ6f6+IeKon+arYw5pSY2pFxrJ5VIu9z3eputEVxZRgAZFxzdpP2zHJ1j5tp -7o0GIuNYPd0i7/NGW+i9hpZ2zIyxY7hHxlbVSkEMk6tbwnU1WxhXlaTjKPSaog6K -qA9RFedo6hPANqBpeMkdDeGyaHYvc8zMlsAzA273GfPHyDvSuiETYjS1Q1lN68a8 -4/GzVLzRcW3DKWHN8sBzLDG8AeL7csILuKcPxqTYCA6OdGvr+d6OAU8usSzu++vN -wOnlHSKTz7qmZ1hjid2gyIHcHKlMNy2L+37KDRWkrKIGzRaGWZJhZ22P5pYaU6yO -y4tuMDafJzv6TdVsYWw/L7Atj5sBXytsfwDspvPk9C7TCoTXp9nCWGULTeS4G/AM -z/LA69OckvI+aFbHAJoVedcqeRwAt2ss4Pr77zEpHdQerjJj4tEUdWCPNYjZ2DI6 -t8+9Xsfig6u+7Zm5bk1ReTFP1os0r9c6804m3SGyOeFyNzC6z5vFZ9avuyLNfc0W -xrrRo3Rmdk1RV3pmrns4bv+TBx65L6zAZwN8ad7JpI01o6dm0eiwbfZ6HYvXa47r -BMv3Bdz1HeH6S9z0RifHM47LjZ3D99ijLYNjgtyv3hZG/diArud7jQ2jCxcBz1gi -oylqSfvm8yCf6xBul2PnvXCDOJCCvrHrvBlW1D20t2sd3bQiz1C8m/PuWPnW870d -Ips13XSxJTh/cSpC062i/DBOg8uJn+tqbtEt0emZmqIWQdRrfB+38gE31o5lWegv -ZmeUWqpY/XjFyyPFHUb9mGmNC7Om42pe3l3tBtwzrcDp5caisRQlK5se3hhkTSfs -6iD84eVdY/k5SEqJoJt7vmYLY0IoMGs6rnFBcVMWPY1o/861WR84+vOEpdnCuMQW -Wr6Te5rwbP1TZk7/lJmzhMfDjkDk3YB7vrFuLP7qj/SZ5em+mc1lorW/8aN0rud7 -t5jZXIZ7252ge0MucITrGytGnyfFIqMPrs5uv5j0WmHb3CvOG3t8Voyy4o9QWaLc -Fh9XwEZwjwS83TNzHcLjWlfetYyxZdlH+F9EXtRsYcjDYqNFY2GzYuZymT5j9rDI -aIoaubBeK43IsvF5ckh+ZYRtc0+zhbF4nE4e4SZj/uiV94yOXhvqN1rGy7qe7zXa -bCHsDNdKzsDDo87OfNdgW9TlZHia+5bm8r2bzb0rnAwfeaA+6tI6dh16mvtWynGd -INrKjQde2EBwL/pv7BnXFB8HwrCvGhe6oC/HteIlQPT/E0EX9OW43pl3MuG6d72V -Efm0VjTNElndFnpG2Lbj2rqZc3qN/qdcp9blZHgqcnaqSJPqNQZ+WmEfkgpTI+W4 -TmD0jCP+4wDSD7CoUct1arYwjPGYEuaOYdQW1wUtLVa7TtBs1BTPBosvjAbL68sF -Qt83r2m+nutx9mmKut7M8npthZPJrPBEdvOy9Vv4jXnuWnxk83yuy9Zw7REEWxjT -S0a0nOn5vIN7gdPlWGbADT7c7+cVh+n5USUbAmLjcBBayXWO74q9XRmzh6c7dVsM -PGkeN9MdSzq4F/hne5D9EaJ1YfnhBcCQzu4jYITZ0GLMLHWqonZwL9goRKZeW5JO -d3DvDLu68+lXi3tBKidERrOFsain3dccoZs5J2ta3Y7Lvb7wLrI/PxV1i9XNs7xe -W5JOX+2Kve7Wvhzf7gTd680sN7YOz8RRKu/HqNJ9q5tnw91M28eQfgQq32gYZNS7 -MmKvJdzAExm9t7mTB2azZpbY4BvbhqfvmMCfSbDucdvxAx4dRn+MoQPoB25il6TT -W0XkZ+P64XNqTKDOGLyiR1PhGhFhqy961so43A0abaH35Du55/KA+/0BCLO0ydgx -fiCjSB3iodIzbFv0n1t35ruKB9ibi97c2pfjxvZx4CgpBWeQq+c8keNe4EQnGrOL -vjBzjm7mA+FbZsZxbb3XiJLl7P3wERJ1m7vcMwOeLi4li4rr4dgaHkVd4WSKRwzL -uJXxjfVD7BV2huu2K/zAsRqzIs0zvi5y3DVzTm9LtKgOPKY25Li7ZOPq3pbSdXRE -PR8l98auWh/QVTS7eey8obFakxGUsHC3V/TlPLGvT7dFY3GKN+YypsujMT3sPbJm -YHVzT++3PRxKBX05nioOpXpbUn5gBnk/ZYk0TzluLh+U+uPT45iL5wJQH4Gi6KnS -W+UQ/7jMDhO4zWiPUmP0LPMDzy89uLQ9sdfoGY/9Y0hlP/B8Pfpgwk/lXd/s4ilb -NBvNmi2MpWOVUApzSS7H3fSKfCaz3sxyY+FYZAzsIMyhzGMCMMCsqMvdfLZey7tZ -0/O7zcwWztPG8rG4oUTGOu77ps2HixkvlKgwDJVx47gyMmpxddHLvYww08NmTTSa -6h9N9bYM/i7pw/vGsWScI7oBpMWZsuIc5fUXmos9bvN9Od3vcwNzn6aolsjmos9M -HNcPzn/rPUSfXtSm2cKYNBRJR4TDqIlea0XKkXu3c+0Vi4Cif5otBjT2I0gWh9bl -/aD/3awS9+c8keVBN8/74TFYl6+Fm7Qx3Rz1n7IMnmeOkKZn035ghjvyUrre7F7T -47oterMi6+i9TtaYpwd9OR6p11qb57ae7VX0R8sOlxpfs4UxI/LKmRLHMnPGrKHR -LM2r/kheWuLDqI5F313oxQvLMEXH1R9+jEA9w23T6kvlos81NFsYDSWeN/f6urnX -b/TTPY220M29vs7ddE44bhD59Gy/VxpNsF78zCX0aenXkPv0gO8LdO5aIu24tu6Y -rum4ab5Py5q5KMTj61lLlsOPlK0HZmeGh1jqx0IfhWjIx0nCssI+5QZfuIOXe6EZ -3NMc1wm0ppGTeKyN12jC+4tJeHc5BkxpPgTTiLPxc8JUFN6PSbOFcXlJvu9L2422 -KPYGOY/nBqNqGJuG19yxIRkhsySapfVqBF0UxYn9W2AnMDt9x3WCkad451xri9cF -Tpdpcc0Wg0p9q5unHdcJBo+KvKApZYuRnz2cLxCmn02Z2XTrXM03DpDhWjTLtLq5 -voYHSz3TcX19tRvwTMZZszrNTaPJmKs160E2p68Ua003radSqWZbRFvPVFSHUpbI -porfhKREwDOpgXu+lJVND3+V6/OEoTTNb21rbbPaWuY1NafTbW3z0un2Vs47m41O -I51u4oZhmVaz1WZcoud9T884nfq+9tZU69zGjOPm9zXabl7POJ2W5gut1VBa061z -O5vbujpNq23+fN7anjbbeGd6/vz2eel01/zOeVZnS9s83mpoo4hLF2Xva29tbJ2r -+UIzDKVlXgtv7my1Ops477Sa2q2ulq6u9maDm7zdnNfcOZd3pVvb53KjYmdv2he7 -jMTOXr/Pt8xMZtf/GwCJfPpKNUAAAA0KLS1jYTkyNjkzMzNkNjgwOGMxY2E4ZmEx -YWJhM2MyYzhmMmViN2Q2ZTBhNjFhNjdkZjA3Nzc5Y2FiZDNjMWINCkNvbnRlbnQt -RGlzcG9zaXRpb246IGZvcm0tZGF0YTsgbmFtZT0ic2FtcGxlX3R5cGVfY29uZmln -IjsgZmlsZW5hbWU9InNhbXBsZV90eXBlX2NvbmZpZy5qc29uIg0KQ29udGVudC1U -eXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0KeyJhbGxvY19vYmplY3Rz -Ijp7InVuaXRzIjoib2JqZWN0cyJ9LCJhbGxvY19zcGFjZSI6eyJ1bml0cyI6ImJ5 -dGVzIn0sImludXNlX29iamVjdHMiOnsidW5pdHMiOiJvYmplY3RzIiwiYWdncmVn -YXRpb24iOiJhdmVyYWdlIn0sImludXNlX3NwYWNlIjp7InVuaXRzIjoiYnl0ZXMi -LCJhZ2dyZWdhdGlvbiI6ImF2ZXJhZ2UifX0NCi0tY2E5MjY5MzMzZDY4MDhjMWNh -OGZhMWFiYTNjMmM4ZjJlYjdkNmUwYTYxYTY3ZGYwNzc3OWNhYmQzYzFiLS0NCg== -******************* -HEADERS: -User-Agent: Go-http-client/1.1 -Content-Length: 1295 -Content-Type: multipart/form-data; boundary=76090d85f36437fddfaea676fd97e09b516b242fff854dbe191df0aeaaa5 -Accept-Encoding: gzip -BODY: -LS03NjA5MGQ4NWYzNjQzN2ZkZGZhZWE2NzZmZDk3ZTA5YjUxNmIyNDJmZmY4NTRk -YmUxOTFkZjBhZWFhYTUNCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsg -bmFtZT0icHJvZmlsZSI7IGZpbGVuYW1lPSJwcm9maWxlLnBwcm9mIg0KQ29udGVu -dC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0KH4sIAAAAAAAE/7RS -TYzbxBf/O46TidN2p9uPneZfgTEXd6XY8SRxkhuqUGErkLghURXveDxx3NieyB+r -LCcfQAKEBFckinrgwgHRcoFTtUIc6AUQBw4IoYpLWe0K8SGQtgUW2UsoH+edyxu9 -937v93u/mcc/vvra27tvfrm3IleBACtyFYiw+tTnP9y8zZ8p7+v5O99U1RUgoK/2 -X6qoEhDgPVGVQAV+0oTCahMIUEKSUtPuiqtNUIF1VFeA9l5ThaCCPvulhIjwA7ns -FWEDNRSgfdtQIRDR3p2yXIWfgrJchTKSFaC9AlQIqui7vRcLQgluH6Al2ERN5Yh2 -S1YhkND261eLcg3+JpboGjyKjirHtK9FFYIauvFTia7DWwfoOlxCS8oRbaexvAQF -WKwl14SKWJVq6gOgjrZ/L8UAeB2qEmjAXaHc8v2DLQGECCo17V242gQNeBwdV5a1 -HUGFAKC37pZMEvxChsLfhtcrIpBq6gpooK2fy+Ey3C+8E+EbcilZhifQCWVZ+15c -PrbQJDUKVOsUEGB+O6+gPN/RlXxdO9k5NRRaLVCBef7yhzfvnEV5vltEJc9l7XRn -pXUGiDDfev6jsnbt1SIquaKhzpnWSVCF+db1H3/dfxDl+Y0iaq1WC0gwz/N7+8UR -UH5tcdX+j/+H6wkJZwFLsER5FqVYpLMMNyMS8YRRHrkJPhVnUeqHTNdWw2RGonO6 -QxKG140JD5kxiXnIn2NTYnjcmE09I+Su4fGARJ7OY89IOQ/ohPjRIxsdvaObbY+b -Ojb1rh74UTZvk9C1ekYSUyPOotQPmRFOGJnpHsfLcRalfsh0L2ab3LnCaIodY3JI -rB4NSTzVPY7/4k0oibhzhdEUL8V/muDRR2PiR/js/cR570kST5/m8ZTF+jiLKMbP -Hpo5Hi0knliwJ5tJysIkJXSK6WGRkiS0SehaPT3BpxfM3j/2vp/XVj163k+Tc7rj -pzP80KJfW/yewuai4QKP1yKXzbFzWLpDx09DUv6lMwsZE0ZmBbsesXl6gSQpfkH4 -N79OCZ0w4yJLzxdvnRhrUcqCwL+45jKCO7inm0YazozH+BMkcg3btk2P207mB67t -+ekkc2zKQztkaexTbvOUBTblQcBoymObhq7NUxbYlAcBoymP7dlmzLHWGVkDa0AH -3X7HdN3BoO+6Q4sxx8QOdt0Ow5gSatIBftjIktgIfMeYDy3b6rUDP8rmbS/KiiTV -E65bWLNcq+eYg7FD6GA0YtbQJQPmuKPRsO+645HTp0530GcW1o0siY3Ad4z/jnPb -gR9l8/Z8aLWL9+c6xlq332WmY1Gnw5hDO0M67o7HQxMzwoakbzo9NnatYY/h2qUN -N+GXsXxpI9lMKAmCy38EAAD//6sEfUGMBgAADQotLTc2MDkwZDg1ZjM2NDM3ZmRk -ZmFlYTY3NmZkOTdlMDliNTE2YjI0MmZmZjg1NGRiZTE5MWRmMGFlYWFhNS0tDQo= -*******************"#; - -const EXPECTED_NAMES: &str = "bufio.(*Writer).Flush, bufio.NewReaderSize, cloud.google.com/go/logging/apiv2/loggingpb.file_google_logging_v2_logging_config_proto_init, cloud.google.com/go/logging/apiv2/loggingpb.init.2, compress/flate.NewWriter, compress/flate.newDeflateFast, crypto/x509.(*CertPool).AddCert, crypto/x509.ParseCertificate, crypto/x509.parseCertificate, crypto/x509/pkix.(*Name).FillFromRDNSequence, github.com/ClickHouse/clickhouse-go/v2.(*clickhouse).Ping, github.com/ClickHouse/clickhouse-go/v2.(*clickhouse).acquire, github.com/ClickHouse/clickhouse-go/v2.(*clickhouse).dial, github.com/ClickHouse/clickhouse-go/v2.(*clickhouse).dial.func1, github.com/ClickHouse/clickhouse-go/v2.DefaultDialStrategy, github.com/ClickHouse/clickhouse-go/v2.dial, github.com/DataDog/datadog-agent/pkg/util/scrubber.AddDefaultReplacers, github.com/DataDog/datadog-agent/pkg/util/scrubber.init.0, github.com/alecthomas/participle/v2.(*generatorContext).parseCapture, github.com/alecthomas/participle/v2.(*generatorContext).parseDisjunction, github.com/alecthomas/participle/v2.(*generatorContext).parseGroup, github.com/alecthomas/participle/v2.(*generatorContext).parseSequence, github.com/alecthomas/participle/v2.(*generatorContext).parseTerm, github.com/alecthomas/participle/v2.(*generatorContext).parseTermNoModifiers, github.com/alecthomas/participle/v2.(*generatorContext).parseType, github.com/alecthomas/participle/v2.(*generatorContext).subparseGroup, github.com/alecthomas/participle/v2.(*structLexer).Next, github.com/alecthomas/participle/v2.(*structLexer).Peek, github.com/alecthomas/participle/v2/lexer.Upgrade, github.com/aws/aws-sdk-go-v2/service/sso/internal/endpoints.init, github.com/aws/aws-sdk-go/aws/endpoints.init, github.com/envoyproxy/go-control-plane/envoy/config/overload/v3.file_envoy_config_overload_v3_overload_proto_init, github.com/envoyproxy/go-control-plane/envoy/config/overload/v3.init.0, github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3.file_envoy_type_matcher_v3_status_code_input_proto_init, github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3.init.7, github.com/goccy/go-json/internal/decoder.init.0, github.com/goccy/go-json/internal/encoder.init.0, github.com/gogo/protobuf/proto.RegisterType, github.com/google/gnostic-models/openapiv3.file_openapiv3_OpenAPIv3_proto_init, github.com/google/gnostic-models/openapiv3.init.0, github.com/google/pprof/profile.init, github.com/gorilla/mux.(*Router).ServeHTTP, github.com/grafana/pyroscope-go.(*Session).Start.func1, github.com/grafana/pyroscope-go.(*Session).Start.func2, github.com/grafana/pyroscope-go.(*Session).dumpHeapProfile, github.com/grafana/pyroscope-go.(*Session).reset, github.com/grafana/pyroscope-go.(*Session).takeSnapshots, github.com/grafana/pyroscope-go.(*Session).uploadData, github.com/grafana/pyroscope-go.(*cpuProfileCollector).Start, github.com/grafana/pyroscope-go.(*cpuProfileCollector).reset, github.com/grafana/pyroscope-go/godeltaprof.(*HeapProfiler).Profile, github.com/grafana/pyroscope-go/godeltaprof/internal/pprof.(*DeltaHeapProfiler).WriteHeapProto, github.com/grafana/pyroscope-go/godeltaprof/internal/pprof.(*profileBuilder).Build, github.com/grafana/pyroscope-go/godeltaprof/internal/pprof.(*profileBuilder).LocsForStack, github.com/grafana/pyroscope-go/godeltaprof/internal/pprof.(*profileBuilder).Sample, github.com/grafana/pyroscope-go/godeltaprof/internal/pprof.(*profileBuilder).emitLocation, github.com/grafana/pyroscope-go/godeltaprof/internal/pprof.(*profileBuilder).flush, github.com/grafana/pyroscope-go/godeltaprof/internal/pprof.(*profileBuilder).stringIndex, github.com/grafana/pyroscope-go/godeltaprof/internal/pprof.(*protobuf).int64s, github.com/grafana/pyroscope-go/internal/pprof.defaultCollector.StartCPUProfile, github.com/grafana/pyroscope-go/upstream/remote.(*Remote).handleJobs, github.com/grafana/pyroscope-go/upstream/remote.(*Remote).safeUpload, github.com/klauspost/compress/flate.(*Writer).Close, github.com/klauspost/compress/flate.(*compressor).close, github.com/klauspost/compress/flate.(*compressor).storeFast, github.com/klauspost/compress/flate.(*fastEncL1).Encode, github.com/klauspost/compress/flate.(*fastGen).addBlock, github.com/klauspost/compress/flate.NewWriter, github.com/klauspost/compress/flate.newFastEnc, github.com/lib/pq.init, github.com/metrico/otel-collector/exporter/clickhouseprofileexporter.createLogsExporter, github.com/metrico/otel-collector/exporter/clickhouseprofileexporter.newClickhouseProfileExporter, github.com/metrico/otel-collector/exporter/clickhouseprofileexporter/ch.NewClickhouseAccessNativeColumnar, github.com/metrico/otel-collector/receiver/pyroscopereceiver.(*pyroscopeReceiver).httpHandlerIngest, github.com/metrico/otel-collector/receiver/pyroscopereceiver.newPyroscopeReceiver.func1, github.com/metrico/otel-collector/receiver/pyroscopereceiver/jfrparser.init, github.com/opencontainers/go-digest.init, github.com/prometheus/procfs.init, github.com/snowflakedb/gosnowflake.init.3, github.com/snowflakedb/gosnowflake.readCACerts, github.com/spf13/cobra.(*Command).Execute, github.com/spf13/cobra.(*Command).ExecuteC, github.com/spf13/cobra.(*Command).execute, github.com/vmware/govmomi/vim25/types.init.2644, github.com/vmware/govmomi/vim25/types.init.3073, github.com/vmware/govmomi/vim25/types.init.6146, github.com/xdg-go/stringprep.init, github.com/xdg-go/stringprep.map.init.2, go.opencensus.io/resource.init, go.opencensus.io/trace/tracestate.init, go.opentelemetry.io/collector/config/confighttp.(*ServerConfig).ToServerContext.maxRequestBodySizeInterceptor.func2, go.opentelemetry.io/collector/config/confighttp.(*clientInfoHandler).ServeHTTP, go.opentelemetry.io/collector/config/confighttp.(*decompressor).ServeHTTP, go.opentelemetry.io/collector/exporter.(*Builder).CreateLogs, go.opentelemetry.io/collector/exporter.CreateLogsFunc.CreateLogsExporter, go.opentelemetry.io/collector/otelcol.(*Collector).Run, go.opentelemetry.io/collector/otelcol.(*Collector).setupConfigurationComponents, go.opentelemetry.io/collector/otelcol.NewCommand.func1, go.opentelemetry.io/collector/service.(*Service).initExtensionsAndPipeline, go.opentelemetry.io/collector/service.New, go.opentelemetry.io/collector/service/internal/graph.(*Graph).buildComponents, go.opentelemetry.io/collector/service/internal/graph.(*exporterNode).buildComponent, go.opentelemetry.io/collector/service/internal/graph.Build, go.opentelemetry.io/collector/service/telemetry.New, go.opentelemetry.io/collector/service/telemetry.newLogger, go.opentelemetry.io/collector/service/telemetry.newSampledLogger, go.opentelemetry.io/collector/service/telemetry.newSampledLogger.WrapCore.func2, go.opentelemetry.io/collector/service/telemetry.newSampledLogger.func1, go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp.(*middleware).serveHTTP, go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp.NewMiddleware.func1.1, go.uber.org/zap.(*Logger).WithOptions, go.uber.org/zap.optionFunc.apply, go.uber.org/zap/zapcore.newCounters, golang.org/x/text/encoding/ianaindex.init, golang.org/x/text/encoding/ianaindex.map.init.0, google.golang.org/protobuf/internal/filedesc.(*File).initDecls, google.golang.org/protobuf/internal/filedesc.(*File).unmarshalSeed, google.golang.org/protobuf/internal/filedesc.(*Message).unmarshalSeed, google.golang.org/protobuf/internal/filedesc.Builder.Build, google.golang.org/protobuf/internal/filedesc.appendFullName, google.golang.org/protobuf/internal/filedesc.newRawFile, google.golang.org/protobuf/internal/filetype.Builder.Build, google.golang.org/protobuf/internal/strs.(*Builder).grow, io.ReadAll, k8s.io/api/admissionregistration/v1beta1.addKnownTypes, k8s.io/api/authorization/v1beta1.init.0, k8s.io/api/autoscaling/v2beta1.init.0, k8s.io/api/core/v1.addKnownTypes, k8s.io/api/flowcontrol/v1beta1.addKnownTypes, k8s.io/apimachinery/pkg/apis/meta/v1.AddToGroupVersion, k8s.io/apimachinery/pkg/runtime.(*Scheme).AddKnownTypeWithName, k8s.io/apimachinery/pkg/runtime.(*Scheme).AddKnownTypes, k8s.io/apimachinery/pkg/runtime.(*SchemeBuilder).AddToScheme, k8s.io/client-go/kubernetes/scheme.init.0, main.run, main.runInteractive, n/a, net.(*conn).Write, net.(*netFD).Write, net/http.(*conn).serve, net/http.(*persistConn).writeLoop, net/http.HandlerFunc.ServeHTTP, net/http.NewRequest, net/http.NewRequestWithContext, net/http.persistConnWriter.Write, net/http.serverHandler.ServeHTTP, os.ReadFile, reflect.(*rtype).Method, reflect.(*rtype).MethodByName, reflect.FuncOf, reflect.Value.MethodByName, reflect.typelinks, reflect.typesByString, regexp.Compile, regexp.compile, regexp.compileOnePass, regexp.makeOnePass, regexp.newQueue, regexp/syntax.(*Regexp).Simplify, regexp/syntax.(*compiler).compile, regexp/syntax.(*compiler).inst, regexp/syntax.Compile, runtime.(*gcBits).bitp, runtime.(*gcWork).tryGet, runtime.(*itabTableType).find, runtime.(*lfstack).pop, runtime.(*lfstack).push, runtime.(*mspan).base, runtime.(*pageAlloc).scavenge, runtime.(*pageAlloc).scavenge.func1, runtime.(*scavengerState).init.func2, runtime.(*scavengerState).run, runtime.(*stkframe).getStackMap, runtime.assertI2I2, runtime.bgscavenge, runtime.doInit, runtime.doInit1, runtime.gcBgMarkWorker, runtime.gcBgMarkWorker.func2, runtime.gcDrain, runtime.getitab, runtime.greyobject, runtime.heapBits.nextFast, runtime.itabsinit, runtime.madvise, runtime.main, runtime.markroot, runtime.markroot.func1, runtime.memmove, runtime.pcdatavalue, runtime.pcvalue, runtime.putempty, runtime.readvarint, runtime.rt0_go, runtime.scanframeworker, runtime.scanobject, runtime.scanstack, runtime.schedinit, runtime.sysUnused, runtime.sysUnusedOS, runtime.systemstack, runtime/internal/syscall.Syscall6, runtime/pprof.(*profMap).lookup, runtime/pprof.(*profileBuilder).addCPUData, runtime/pprof.(*profileBuilder).build, runtime/pprof.(*profileBuilder).readMapping, runtime/pprof.StartCPUProfile, runtime/pprof.newProfileBuilder, runtime/pprof.profileWriter, syscall.RawSyscall6, syscall.Syscall, syscall.Write, syscall.write, total"; -const EXPECTED_MAP: &str = r#"0: [38742454760335319, 40087946534119400, 42596367897004777, 48771580209901686, 53990741595935099, 57088183000490135, 58557529421486831, 59457293053784180, 59593299692301340, 66048360549176157, 67085792033845063, 67191441878081537, 67662774102607196, 69612847511641035] -38742454760335319: [100628372814638195] -40087946534119400: [79270644668093111] -42596367897004777: [101487633555280136] -48771580209901686: [94918361600696052] -53990741595935099: [107434199424883752, 89550126155032432, 94200449688993973] -57088183000490135: [99612870642839279] -58557529421486831: [76842973892222616] -59457293053784180: [100063261950489098] -59593299692301340: [91363841374301870] -66048360549176157: [97043151921186953] -67085792033845063: [98457092565724063] -67191441878081537: [80234187892307127] -67662774102607196: [100070498862204333] -69612847511641035: [93989015620725212] -76842973892222616: [141511466637999628] -79270644668093111: [140046790225519686] -80234187892307127: [108446884271945857, 113267436422830507, 114248832421025570, 114885430980069358, 115331698473596966, 117892689794117372, 119726413661300012, 121372394715467071, 125414451763041269, 128617529603403115, 130674963834653131, 131369017856162869, 131377443943695927, 133162168510851715, 133224444749139462, 133456625119750025, 133670685672466743, 135704509429414406, 136256372519083004, 136369426990552861, 136900252284730992, 138350841599489442, 141632261736556477, 141644186349176910, 142921258866238155] -89550126155032432: [132968328674819128] -91363841374301870: [112927787583348828] -93989015620725212: [123859131567339026] -94200449688993973: [111305032523588391] -94918361600696052: [115517637213504835] -97043151921186953: [138755457801079417] -98457092565724063: [131444219727285889] -99612870642839279: [143056642792749795] -100063261950489098: [114239512416445153] -100070498862204333: [113036276560317227] -100628372814638195: [113202375439754492] -101487633555280136: [138302629414163253] -107434199424883752: [143716402966321512] -108446884271945857: [164598631465211387] -113036276560317227: [162585562039257624] -113202375439754492: [144907925925522891] -114239512416445153: [176508602522439059] -114248832421025570: [179755313256235787] -115331698473596966: [162678103806317869] -115517637213504835: [166486937955124181] -117892689794117372: [168707815736043364] -119726413661300012: [151672128231048010] -121372394715467071: [146560668754513061] -123859131567339026: [167803307762682777] -125414451763041269: [150771910060347665] -128617529603403115: [149591371078846279] -130674963834653131: [152957885916485528] -131377443943695927: [171871083424469921] -131444219727285889: [158560652969149676] -132968328674819128: [174607084807025992] -133162168510851715: [160368297165868928] -133224444749139462: [166300091472105835] -133456625119750025: [171820145069951969] -135704509429414406: [177677207921798470] -136256372519083004: [172843056586582600] -136369426990552861: [171635302867278403] -136900252284730992: [159371476378974887] -138302629414163253: [152091754849386483] -138350841599489442: [170753895802118158] -138755457801079417: [169936498903112440] -140046790225519686: [162052294746411150] -141511466637999628: [154563265271950256] -141644186349176910: [172439205468229740] -143056642792749795: [151373822819177589] -143716402966321512: [147895167036604141] -144907925925522891: [211823748967180240] -146560668754513061: [202372133294706040] -149591371078846279: [183845667997627588] -151373822819177589: [204504177385824931] -151672128231048010: [203035540117135618] -152091754849386483: [190431325594563993] -154563265271950256: [202382720767460487] -158560652969149676: [203771058446615966] -159371476378974887: [180529091226645867] -160368297165868928: [209291742825885717] -162052294746411150: [181754272285663650] -162585562039257624: [205012902263967563, 206106656114774191, 208139625951028649, 213747929336669041] -162678103806317869: [193609792679648060] -166300091472105835: [203175429227652691] -166486937955124181: [214733506333145550] -167803307762682777: [212288632265080590] -168707815736043364: [200420868017042899] -169936498903112440: [207016626847385621] -170753895802118158: [207696567357905885, 211229286556507985] -171635302867278403: [215891770457407678] -171820145069951969: [198185094474001340] -171871083424469921: [202139988629356393] -172439205468229740: [184990442633448882] -172843056586582600: [202028551340529695] -176508602522439059: [192684785179366918] -177677207921798470: [212698517895877695] -180529091226645867: [234212302893142491] -181754272285663650: [223163788020989333] -183845667997627588: [218657610839969430] -184990442633448882: [244915317080401139] -190431325594563993: [243728370117120237] -192684785179366918: [230844740840469675] -193609792679648060: [237537306502838130] -198185094474001340: [243079623770450903] -200420868017042899: [223583814222236675] -202028551340529695: [243525418435742126] -202139988629356393: [217731971905874853] -202372133294706040: [241945877256331538] -202382720767460487: [231336693026650961] -203035540117135618: [248986284621543964] -203175429227652691: [232498376900343756] -203771058446615966: [241946918561780749] -205012902263967563: [222240240991495290, 228138096816433381, 229786537842853059] -206106656114774191: [231182262209661194] -207016626847385621: [245709583625582887] -209291742825885717: [235127192721045853] -211229286556507985: [225119056699523582] -211823748967180240: [227812771612588225] -212288632265080590: [225741202102833501] -212698517895877695: [223622488125970319, 226074041614824704, 226326886345647069] -213747929336669041: [242278203730768664, 246612818468819164] -214733506333145550: [248406561647853472] -215891770457407678: [249196627458800899] -217731971905874853: [280131986563362610] -218657610839969430: [267039375489569399] -223163788020989333: [260677813958852540] -223583814222236675: [256498835618521422] -223622488125970319: [260571114322058968] -225119056699523582: [256039287245742699] -225741202102833501: [254727702034572013] -226074041614824704: [287930622953249787] -226326886345647069: [271805855373081495] -227812771612588225: [257319139697943423] -230844740840469675: [276073338854032635] -231182262209661194: [281236825529333440] -231336693026650961: [278691972420507434] -232498376900343756: [261144471606523809] -234212302893142491: [257783987186986042] -235127192721045853: [273578698003240119] -237537306502838130: [285763816937176870] -241945877256331538: [268438102404688691] -241946918561780749: [257241256473655176] -243525418435742126: [276134307233260561] -243728370117120237: [281969803670885355] -244915317080401139: [269936810596416513] -245709583625582887: [257463554105572768] -246612818468819164: [277288251556112728] -248406561647853472: [252569824539384422] -248986284621543964: [261400615560482546] -249196627458800899: [272798692730783874] -252569824539384422: [305292813914654951] -256498835618521422: [312763589063956012] -257241256473655176: [306111611259841957] -257319139697943423: [299082446479950134] -257463554105572768: [310279315742847272] -257783987186986042: [320401531452854762] -260571114322058968: [309455851780254571] -260677813958852540: [296247052900183451, 317744443962260464, 323186131871480845] -267039375489569399: [319439935469660530] -269936810596416513: [308011307883155678] -271805855373081495: [295467226836533886] -273578698003240119: [320300640419126287] -276073338854032635: [319187190984906169] -278691972420507434: [323560718645185674] -281236825529333440: [306077811224786879] -281969803670885355: [315953078079461946] -285763816937176870: [316913844144087631] -287930622953249787: [303827084499332991] -295467226836533886: [326409636778730601] -296247052900183451: [349618734685561198, 359288355374643511] -299082446479950134: [331359355115483339] -303827084499332991: [338780526052653927] -305292813914654951: [350889042354921962] -306077811224786879: [340916933448816229] -306111611259841957: [327550878786529068] -310279315742847272: [336163923347285241] -312763589063956012: [326613319977195793] -315953078079461946: [325145473923500605] -317744443962260464: [350168639796857360, 359131412474089459] -319187190984906169: [339354010836253434] -319439935469660530: [345933865856257569] -320300640419126287: [328924661850434419] -323186131871480845: [345577853602457116, 357491054556179667] -323560718645185674: [345639817392002662] -325145473923500605: [365276176966701378] -327550878786529068: [382409820864484947] -331359355115483339: [396161431009652089] -336163923347285241: [361022495174443294] -338780526052653927: [385220733308424239] -339354010836253434: [390788673691463970] -340916933448816229: [393832446376904874] -345577853602457116: [367001874425340950] -345639817392002662: [392863616087521191] -345933865856257569: [363770127908956666] -349618734685561198: [395656354623906696] -350168639796857360: [395541453753437426] -357491054556179667: [386153478011204969] -361022495174443294: [426468028027318203] -363770127908956666: [413105119106341518] -365276176966701378: [414131856178699368, 427478550692068623] -385220733308424239: [411557834771911607] -390788673691463970: [416118621215589990] -392863616087521191: [429645746188757495] -393832446376904874: [401174625198593520] -395541453753437426: [419391523947149865] -395656354623906696: [425632162216103475] -396161431009652089: [431936995650807243] -401174625198593520: [456447508180047158] -411557834771911607: [437861033291124710] -414131856178699368: [437033094921809140] -416118621215589990: [446096569348012462] -419391523947149865: [465704827954701504] -425632162216103475: [437489796624700501] -426468028027318203: [452497480807275245] -427478550692068623: [448483283656531511] -429645746188757495: [435023920018836649] -431936995650807243: [463212886879666703] -435023920018836649: [497604134777822163] -437033094921809140: [472676070278615598] -437861033291124710: [488097452589611931] -446096569348012462: [486403981884537998] -448483283656531511: [485108097661571896] -452497480807275245: [499306537905829224] -463212886879666703: [478497921702831780] -465704827954701504: [474155843758329780] -472676070278615598: [514941220186423434] -478497921702831780: [507613228799022537] -485108097661571896: [521502711493770737] -486403981884537998: [539125783741006397] -488097452589611931: [510679870848207228] -497604134777822163: [539770343320994095] -499306537905829224: [517534758553984568] -507613228799022537: [544973614009445412] -514941220186423434: [550526413983358244] -521502711493770737: [552048866168332286] -539125783741006397: [553053910897306124] -539770343320994095: [568796492124626177] -544973614009445412: [603048788829292163] -550526413983358244: [583586592627894792] -552048866168332286: [592383109338724224] -553053910897306124: [595826071670654526] -568796492124626177: [589612847844729594] -583586592627894792: [641396004492829198] -589612847844729594: [639492022376244668] -592383109338724224: [631152865748449390] -595826071670654526: [623605633815227692] -603048788829292163: [642148023643848224] -623605633815227692: [663812272016721927] -631152865748449390: [657922135552082607] -639492022376244668: [684347966288065243] -641396004492829198: [659329956224601251] -642148023643848224: [658011455666624803] -658011455666624803: [701377244087752852] -659329956224601251: [710883616105928487] -663812272016721927: [692611675432640454] -684347966288065243: [709834451815324138] -692611675432640454: [727858206006214905] -701377244087752852: [744127002732167100] -709834451815324138: [728259091397428983] -710883616105928487: [741318177634328511] -727858206006214905: [772615364345543035] -728259091397428983: [771868168823000744] -741318177634328511: [760902393548738380] -744127002732167100: [789103945439592791] -760902393548738380: [802556183189320716] -771868168823000744: [807210588330467000] -772615364345543035: [819758594897649876] -789103945439592791: [802714868440700285] -802556183189320716: [856519119881828781] -802714868440700285: [842366458003816992] -807210588330467000: [858208801750801133] -819758594897649876: [860849857165541675] -842366458003816992: [872896174453274103] -856519119881828781: [898702575464302642] -858208801750801133: [882798593320449911] -860849857165541675: [867629405263846024] -867629405263846024: [925077370316601880] -872896174453274103: [925302796778635955] -882798593320449911: [928341377466988897] -898702575464302642: [910131451510442396] -910131451510442396: [967539570511740301] -925077370316601880: [936830443724683529] -925302796778635955: [942446406691314293] -928341377466988897: [937034112021755462] -936830443724683529: [978337694696331794] -937034112021755462: [1008333201575256740] -942446406691314293: [973646071648783645] -973646071648783645: [1039417800972441990] -978337694696331794: [1019903727548730494] -1008333201575256740: [1025894988602438855] -1019903727548730494: [1047856586150546435] -1025894988602438855: [1069379696897221201] -1039417800972441990: [1062132269117042055] -1047856586150546435: [1094195406916561896] -1062132269117042055: [1094103587605650923] -1069379696897221201: [1093278396076019935] -1093278396076019935: [1124970154624221772] -1094103587605650923: [1128890574693008158] -1094195406916561896: [1120238951528850364, 1144429090257526679] -1120238951528850364: [1177891509056271968] -1124970154624221772: [1181139411236334438] -1144429090257526679: [1179266705547649400] -"#; diff --git a/pyroscope/pprof.js b/pyroscope/pprof.js deleted file mode 100644 index 144e3f3d..00000000 --- a/pyroscope/pprof.js +++ /dev/null @@ -1,151 +0,0 @@ -const messages = require('./profile_pb') - -/** - * - * @param buf {Uint8Array} - * @returns {*} - * @constructor - */ -const readULeb32 = (buf, start) => { - let res = 0 - let i = start - for (; (buf[i] & 0x80) === 0x80; i++) { - res |= (buf[i] & 0x7f) << ((i - start) * 7) - } - res |= (buf[i] & 0x7f) << ((i - start) * 7) - return [res, i - start + 1] -} - -class TreeNode { - constructor (nameIdx, total, self, children) { - this.nameIdx = nameIdx || 0 - this.prepend = BigInt(0) - this.total = total || BigInt(0) - this.self = self || BigInt(0) - this.children = children || [] - } -} - -class Tree { - constructor () { - this.names = ['total'] - this.namesMap = { total: 0 } - this.root = new TreeNode() - this.sampleType = [] - this.maxSelf = BigInt(0) - } - - /** - * - * @param {Profile} prof - */ - merge (prof) { - const functions = prof.getFunctionList().reduce((a, b) => { - a[b.getId()] = prof.getStringTableList()[b.getName()] - return a - }, {}) - - const locations = prof.getLocationList().reduce((a, b) => { - a[b.getId()] = b - return a - }, {}) - const getFnName = (l) => functions[l.getLineList()[0].getFunctionId()] - - const valueIdx = prof.getSampleTypeList().findIndex((type) => - this.sampleType === `${prof.getStringTableList()[type.getType()]}:${prof.getStringTableList()[type.getUnit()]}` - ) - - for (const l of prof.getLocationList()) { - const line = getFnName(l) - if (this.namesMap[line]) { - continue - } - this.names.push(line) - this.namesMap[line] = this.names.length - 1 - } - for (const s of prof.getSampleList()) { - let node = this.root - for (let i = s.getLocationIdList().length - 1; i >= 0; i--) { - const location = locations[s.getLocationIdList()[i]] - const nameIdx = this.namesMap[getFnName(location)] - let nodeIdx = node.children.findIndex(c => c.nameIdx === nameIdx) - if (nodeIdx === -1) { - node.children.push(new TreeNode(nameIdx)) - nodeIdx = node.children.length - 1 - } - node = node.children[nodeIdx] - node.total += BigInt(s.getValueList()[valueIdx]) - if (i === 0) { - node.self += BigInt(s.getValueList()[valueIdx]) - if (node.self > this.maxSelf) { - this.maxSelf = node.self - } - } - } - } - this.root.total = this.root.children.reduce((a, b) => a + b.total, BigInt(0)) - } -} - -/** - * - * @param t {Tree} - * @returns {BigInt[][]} - */ -const bfs = (t) => { - let refs = [t.root] - let validRefs = true - let prepend = BigInt(0) - const putPrepend = (v) => { - prepend += v - } - const getPrepend = () => { - const res = prepend - prepend = BigInt(0) - return res - } - const res = [[0, parseInt(t.root.total), parseInt(t.root.self), t.root.nameIdx]] - for (;validRefs;) { - validRefs = false - getPrepend() - const _res = [] - const _refs = [] - for (const r of refs) { - putPrepend(r.prepend) - for (const c of r.children) { - validRefs = true - c.prepend = getPrepend() - _res.push(parseInt(c.prepend), parseInt(c.total), parseInt(c.self), c.nameIdx) - } - _refs.push.apply(_refs, r.children) - if (r.children.length === 0) { - putPrepend(r.total) - } else { - putPrepend(r.self) - } - } - res.push(_res) - refs = _refs - } - return res -} - -/** - * - * @param {Uint8Array[]} pprofBinaries - * @param {string} sampleType - */ -const createFlameGraph = (pprofBinaries, sampleType) => { - const tree = new Tree() - tree.sampleType = sampleType - let start = Date.now() - for (const p of pprofBinaries) { - const prof = messages.Profile.deserializeBinary(p) - tree.merge(prof) - } - start = Date.now() - const levels = bfs(tree) - return { levels: levels, names: tree.names, total: parseInt(tree.root.total), maxSelf: parseInt(tree.maxSelf) } -} - -module.exports = { createFlameGraph, readULeb32 } diff --git a/pyroscope/profile_pb.js b/pyroscope/profile_pb.js deleted file mode 100644 index f174224a..00000000 --- a/pyroscope/profile_pb.js +++ /dev/null @@ -1,2665 +0,0 @@ -// source: proto/profile.proto -/** - * @fileoverview - * @enhanceable - * @suppress {missingRequire} reports error on implicit type usages. - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! -/* eslint-disable */ -// @ts-nocheck - -var jspb = require('google-protobuf'); -var goog = jspb; -var global = - (typeof globalThis !== 'undefined' && globalThis) || - (typeof window !== 'undefined' && window) || - (typeof global !== 'undefined' && global) || - (typeof self !== 'undefined' && self) || - (function () { return this; }).call(null) || - Function('return this')(); - -goog.exportSymbol('proto.perftools.profiles.Function', null, global); -goog.exportSymbol('proto.perftools.profiles.Label', null, global); -goog.exportSymbol('proto.perftools.profiles.Line', null, global); -goog.exportSymbol('proto.perftools.profiles.Location', null, global); -goog.exportSymbol('proto.perftools.profiles.Mapping', null, global); -goog.exportSymbol('proto.perftools.profiles.Profile', null, global); -goog.exportSymbol('proto.perftools.profiles.Sample', null, global); -goog.exportSymbol('proto.perftools.profiles.ValueType', null, global); -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.perftools.profiles.Profile = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.perftools.profiles.Profile.repeatedFields_, null); -}; -goog.inherits(proto.perftools.profiles.Profile, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.perftools.profiles.Profile.displayName = 'proto.perftools.profiles.Profile'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.perftools.profiles.ValueType = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.perftools.profiles.ValueType, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.perftools.profiles.ValueType.displayName = 'proto.perftools.profiles.ValueType'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.perftools.profiles.Sample = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.perftools.profiles.Sample.repeatedFields_, null); -}; -goog.inherits(proto.perftools.profiles.Sample, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.perftools.profiles.Sample.displayName = 'proto.perftools.profiles.Sample'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.perftools.profiles.Label = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.perftools.profiles.Label, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.perftools.profiles.Label.displayName = 'proto.perftools.profiles.Label'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.perftools.profiles.Mapping = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.perftools.profiles.Mapping, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.perftools.profiles.Mapping.displayName = 'proto.perftools.profiles.Mapping'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.perftools.profiles.Location = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.perftools.profiles.Location.repeatedFields_, null); -}; -goog.inherits(proto.perftools.profiles.Location, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.perftools.profiles.Location.displayName = 'proto.perftools.profiles.Location'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.perftools.profiles.Line = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.perftools.profiles.Line, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.perftools.profiles.Line.displayName = 'proto.perftools.profiles.Line'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.perftools.profiles.Function = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.perftools.profiles.Function, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.perftools.profiles.Function.displayName = 'proto.perftools.profiles.Function'; -} - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.perftools.profiles.Profile.repeatedFields_ = [1,2,3,4,5,6,13]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.perftools.profiles.Profile.prototype.toObject = function(opt_includeInstance) { - return proto.perftools.profiles.Profile.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.perftools.profiles.Profile} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.perftools.profiles.Profile.toObject = function(includeInstance, msg) { - var f, obj = { - sampleTypeList: jspb.Message.toObjectList(msg.getSampleTypeList(), - proto.perftools.profiles.ValueType.toObject, includeInstance), - sampleList: jspb.Message.toObjectList(msg.getSampleList(), - proto.perftools.profiles.Sample.toObject, includeInstance), - mappingList: jspb.Message.toObjectList(msg.getMappingList(), - proto.perftools.profiles.Mapping.toObject, includeInstance), - locationList: jspb.Message.toObjectList(msg.getLocationList(), - proto.perftools.profiles.Location.toObject, includeInstance), - functionList: jspb.Message.toObjectList(msg.getFunctionList(), - proto.perftools.profiles.Function.toObject, includeInstance), - stringTableList: (f = jspb.Message.getRepeatedField(msg, 6)) == null ? undefined : f, - dropFrames: jspb.Message.getFieldWithDefault(msg, 7, 0), - keepFrames: jspb.Message.getFieldWithDefault(msg, 8, 0), - timeNanos: jspb.Message.getFieldWithDefault(msg, 9, 0), - durationNanos: jspb.Message.getFieldWithDefault(msg, 10, 0), - periodType: (f = msg.getPeriodType()) && proto.perftools.profiles.ValueType.toObject(includeInstance, f), - period: jspb.Message.getFieldWithDefault(msg, 12, 0), - commentList: (f = jspb.Message.getRepeatedField(msg, 13)) == null ? undefined : f, - defaultSampleType: jspb.Message.getFieldWithDefault(msg, 14, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.perftools.profiles.Profile} - */ -proto.perftools.profiles.Profile.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.perftools.profiles.Profile; - return proto.perftools.profiles.Profile.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.perftools.profiles.Profile} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.perftools.profiles.Profile} - */ -proto.perftools.profiles.Profile.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = new proto.perftools.profiles.ValueType; - reader.readMessage(value,proto.perftools.profiles.ValueType.deserializeBinaryFromReader); - msg.addSampleType(value); - break; - case 2: - var value = new proto.perftools.profiles.Sample; - reader.readMessage(value,proto.perftools.profiles.Sample.deserializeBinaryFromReader); - msg.addSample(value); - break; - case 3: - var value = new proto.perftools.profiles.Mapping; - reader.readMessage(value,proto.perftools.profiles.Mapping.deserializeBinaryFromReader); - msg.addMapping(value); - break; - case 4: - var value = new proto.perftools.profiles.Location; - reader.readMessage(value,proto.perftools.profiles.Location.deserializeBinaryFromReader); - msg.addLocation(value); - break; - case 5: - var value = new proto.perftools.profiles.Function; - reader.readMessage(value,proto.perftools.profiles.Function.deserializeBinaryFromReader); - msg.addFunction(value); - break; - case 6: - var value = /** @type {string} */ (reader.readString()); - msg.addStringTable(value); - break; - case 7: - var value = /** @type {number} */ (reader.readInt64()); - msg.setDropFrames(value); - break; - case 8: - var value = /** @type {number} */ (reader.readInt64()); - msg.setKeepFrames(value); - break; - case 9: - var value = /** @type {number} */ (reader.readInt64()); - msg.setTimeNanos(value); - break; - case 10: - var value = /** @type {number} */ (reader.readInt64()); - msg.setDurationNanos(value); - break; - case 11: - var value = new proto.perftools.profiles.ValueType; - reader.readMessage(value,proto.perftools.profiles.ValueType.deserializeBinaryFromReader); - msg.setPeriodType(value); - break; - case 12: - var value = /** @type {number} */ (reader.readInt64()); - msg.setPeriod(value); - break; - case 13: - var values = /** @type {!Array<number>} */ (reader.isDelimited() ? reader.readPackedInt64() : [reader.readInt64()]); - for (var i = 0; i < values.length; i++) { - msg.addComment(values[i]); - } - break; - case 14: - var value = /** @type {number} */ (reader.readInt64()); - msg.setDefaultSampleType(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.perftools.profiles.Profile.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.perftools.profiles.Profile.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.perftools.profiles.Profile} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.perftools.profiles.Profile.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getSampleTypeList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 1, - f, - proto.perftools.profiles.ValueType.serializeBinaryToWriter - ); - } - f = message.getSampleList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 2, - f, - proto.perftools.profiles.Sample.serializeBinaryToWriter - ); - } - f = message.getMappingList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 3, - f, - proto.perftools.profiles.Mapping.serializeBinaryToWriter - ); - } - f = message.getLocationList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 4, - f, - proto.perftools.profiles.Location.serializeBinaryToWriter - ); - } - f = message.getFunctionList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 5, - f, - proto.perftools.profiles.Function.serializeBinaryToWriter - ); - } - f = message.getStringTableList(); - if (f.length > 0) { - writer.writeRepeatedString( - 6, - f - ); - } - f = message.getDropFrames(); - if (f !== 0) { - writer.writeInt64( - 7, - f - ); - } - f = message.getKeepFrames(); - if (f !== 0) { - writer.writeInt64( - 8, - f - ); - } - f = message.getTimeNanos(); - if (f !== 0) { - writer.writeInt64( - 9, - f - ); - } - f = message.getDurationNanos(); - if (f !== 0) { - writer.writeInt64( - 10, - f - ); - } - f = message.getPeriodType(); - if (f != null) { - writer.writeMessage( - 11, - f, - proto.perftools.profiles.ValueType.serializeBinaryToWriter - ); - } - f = message.getPeriod(); - if (f !== 0) { - writer.writeInt64( - 12, - f - ); - } - f = message.getCommentList(); - if (f.length > 0) { - writer.writePackedInt64( - 13, - f - ); - } - f = message.getDefaultSampleType(); - if (f !== 0) { - writer.writeInt64( - 14, - f - ); - } -}; - - -/** - * repeated ValueType sample_type = 1; - * @return {!Array<!proto.perftools.profiles.ValueType>} - */ -proto.perftools.profiles.Profile.prototype.getSampleTypeList = function() { - return /** @type{!Array<!proto.perftools.profiles.ValueType>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.perftools.profiles.ValueType, 1)); -}; - - -/** - * @param {!Array<!proto.perftools.profiles.ValueType>} value - * @return {!proto.perftools.profiles.Profile} returns this -*/ -proto.perftools.profiles.Profile.prototype.setSampleTypeList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 1, value); -}; - - -/** - * @param {!proto.perftools.profiles.ValueType=} opt_value - * @param {number=} opt_index - * @return {!proto.perftools.profiles.ValueType} - */ -proto.perftools.profiles.Profile.prototype.addSampleType = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.perftools.profiles.ValueType, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.clearSampleTypeList = function() { - return this.setSampleTypeList([]); -}; - - -/** - * repeated Sample sample = 2; - * @return {!Array<!proto.perftools.profiles.Sample>} - */ -proto.perftools.profiles.Profile.prototype.getSampleList = function() { - return /** @type{!Array<!proto.perftools.profiles.Sample>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.perftools.profiles.Sample, 2)); -}; - - -/** - * @param {!Array<!proto.perftools.profiles.Sample>} value - * @return {!proto.perftools.profiles.Profile} returns this -*/ -proto.perftools.profiles.Profile.prototype.setSampleList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 2, value); -}; - - -/** - * @param {!proto.perftools.profiles.Sample=} opt_value - * @param {number=} opt_index - * @return {!proto.perftools.profiles.Sample} - */ -proto.perftools.profiles.Profile.prototype.addSample = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 2, opt_value, proto.perftools.profiles.Sample, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.clearSampleList = function() { - return this.setSampleList([]); -}; - - -/** - * repeated Mapping mapping = 3; - * @return {!Array<!proto.perftools.profiles.Mapping>} - */ -proto.perftools.profiles.Profile.prototype.getMappingList = function() { - return /** @type{!Array<!proto.perftools.profiles.Mapping>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.perftools.profiles.Mapping, 3)); -}; - - -/** - * @param {!Array<!proto.perftools.profiles.Mapping>} value - * @return {!proto.perftools.profiles.Profile} returns this -*/ -proto.perftools.profiles.Profile.prototype.setMappingList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 3, value); -}; - - -/** - * @param {!proto.perftools.profiles.Mapping=} opt_value - * @param {number=} opt_index - * @return {!proto.perftools.profiles.Mapping} - */ -proto.perftools.profiles.Profile.prototype.addMapping = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 3, opt_value, proto.perftools.profiles.Mapping, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.clearMappingList = function() { - return this.setMappingList([]); -}; - - -/** - * repeated Location location = 4; - * @return {!Array<!proto.perftools.profiles.Location>} - */ -proto.perftools.profiles.Profile.prototype.getLocationList = function() { - return /** @type{!Array<!proto.perftools.profiles.Location>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.perftools.profiles.Location, 4)); -}; - - -/** - * @param {!Array<!proto.perftools.profiles.Location>} value - * @return {!proto.perftools.profiles.Profile} returns this -*/ -proto.perftools.profiles.Profile.prototype.setLocationList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 4, value); -}; - - -/** - * @param {!proto.perftools.profiles.Location=} opt_value - * @param {number=} opt_index - * @return {!proto.perftools.profiles.Location} - */ -proto.perftools.profiles.Profile.prototype.addLocation = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 4, opt_value, proto.perftools.profiles.Location, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.clearLocationList = function() { - return this.setLocationList([]); -}; - - -/** - * repeated Function function = 5; - * @return {!Array<!proto.perftools.profiles.Function>} - */ -proto.perftools.profiles.Profile.prototype.getFunctionList = function() { - return /** @type{!Array<!proto.perftools.profiles.Function>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.perftools.profiles.Function, 5)); -}; - - -/** - * @param {!Array<!proto.perftools.profiles.Function>} value - * @return {!proto.perftools.profiles.Profile} returns this -*/ -proto.perftools.profiles.Profile.prototype.setFunctionList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 5, value); -}; - - -/** - * @param {!proto.perftools.profiles.Function=} opt_value - * @param {number=} opt_index - * @return {!proto.perftools.profiles.Function} - */ -proto.perftools.profiles.Profile.prototype.addFunction = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 5, opt_value, proto.perftools.profiles.Function, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.clearFunctionList = function() { - return this.setFunctionList([]); -}; - - -/** - * repeated string string_table = 6; - * @return {!Array<string>} - */ -proto.perftools.profiles.Profile.prototype.getStringTableList = function() { - return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 6)); -}; - - -/** - * @param {!Array<string>} value - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.setStringTableList = function(value) { - return jspb.Message.setField(this, 6, value || []); -}; - - -/** - * @param {string} value - * @param {number=} opt_index - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.addStringTable = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 6, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.clearStringTableList = function() { - return this.setStringTableList([]); -}; - - -/** - * optional int64 drop_frames = 7; - * @return {number} - */ -proto.perftools.profiles.Profile.prototype.getDropFrames = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.setDropFrames = function(value) { - return jspb.Message.setProto3IntField(this, 7, value); -}; - - -/** - * optional int64 keep_frames = 8; - * @return {number} - */ -proto.perftools.profiles.Profile.prototype.getKeepFrames = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 8, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.setKeepFrames = function(value) { - return jspb.Message.setProto3IntField(this, 8, value); -}; - - -/** - * optional int64 time_nanos = 9; - * @return {number} - */ -proto.perftools.profiles.Profile.prototype.getTimeNanos = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 9, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.setTimeNanos = function(value) { - return jspb.Message.setProto3IntField(this, 9, value); -}; - - -/** - * optional int64 duration_nanos = 10; - * @return {number} - */ -proto.perftools.profiles.Profile.prototype.getDurationNanos = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 10, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.setDurationNanos = function(value) { - return jspb.Message.setProto3IntField(this, 10, value); -}; - - -/** - * optional ValueType period_type = 11; - * @return {?proto.perftools.profiles.ValueType} - */ -proto.perftools.profiles.Profile.prototype.getPeriodType = function() { - return /** @type{?proto.perftools.profiles.ValueType} */ ( - jspb.Message.getWrapperField(this, proto.perftools.profiles.ValueType, 11)); -}; - - -/** - * @param {?proto.perftools.profiles.ValueType|undefined} value - * @return {!proto.perftools.profiles.Profile} returns this -*/ -proto.perftools.profiles.Profile.prototype.setPeriodType = function(value) { - return jspb.Message.setWrapperField(this, 11, value); -}; - - -/** - * Clears the message field making it undefined. - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.clearPeriodType = function() { - return this.setPeriodType(undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.perftools.profiles.Profile.prototype.hasPeriodType = function() { - return jspb.Message.getField(this, 11) != null; -}; - - -/** - * optional int64 period = 12; - * @return {number} - */ -proto.perftools.profiles.Profile.prototype.getPeriod = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 12, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.setPeriod = function(value) { - return jspb.Message.setProto3IntField(this, 12, value); -}; - - -/** - * repeated int64 comment = 13; - * @return {!Array<number>} - */ -proto.perftools.profiles.Profile.prototype.getCommentList = function() { - return /** @type {!Array<number>} */ (jspb.Message.getRepeatedField(this, 13)); -}; - - -/** - * @param {!Array<number>} value - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.setCommentList = function(value) { - return jspb.Message.setField(this, 13, value || []); -}; - - -/** - * @param {number} value - * @param {number=} opt_index - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.addComment = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 13, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.clearCommentList = function() { - return this.setCommentList([]); -}; - - -/** - * optional int64 default_sample_type = 14; - * @return {number} - */ -proto.perftools.profiles.Profile.prototype.getDefaultSampleType = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 14, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Profile} returns this - */ -proto.perftools.profiles.Profile.prototype.setDefaultSampleType = function(value) { - return jspb.Message.setProto3IntField(this, 14, value); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.perftools.profiles.ValueType.prototype.toObject = function(opt_includeInstance) { - return proto.perftools.profiles.ValueType.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.perftools.profiles.ValueType} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.perftools.profiles.ValueType.toObject = function(includeInstance, msg) { - var f, obj = { - type: jspb.Message.getFieldWithDefault(msg, 1, 0), - unit: jspb.Message.getFieldWithDefault(msg, 2, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.perftools.profiles.ValueType} - */ -proto.perftools.profiles.ValueType.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.perftools.profiles.ValueType; - return proto.perftools.profiles.ValueType.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.perftools.profiles.ValueType} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.perftools.profiles.ValueType} - */ -proto.perftools.profiles.ValueType.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt64()); - msg.setType(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt64()); - msg.setUnit(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.perftools.profiles.ValueType.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.perftools.profiles.ValueType.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.perftools.profiles.ValueType} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.perftools.profiles.ValueType.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getType(); - if (f !== 0) { - writer.writeInt64( - 1, - f - ); - } - f = message.getUnit(); - if (f !== 0) { - writer.writeInt64( - 2, - f - ); - } -}; - - -/** - * optional int64 type = 1; - * @return {number} - */ -proto.perftools.profiles.ValueType.prototype.getType = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.ValueType} returns this - */ -proto.perftools.profiles.ValueType.prototype.setType = function(value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - - -/** - * optional int64 unit = 2; - * @return {number} - */ -proto.perftools.profiles.ValueType.prototype.getUnit = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.ValueType} returns this - */ -proto.perftools.profiles.ValueType.prototype.setUnit = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.perftools.profiles.Sample.repeatedFields_ = [1,2,3]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.perftools.profiles.Sample.prototype.toObject = function(opt_includeInstance) { - return proto.perftools.profiles.Sample.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.perftools.profiles.Sample} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.perftools.profiles.Sample.toObject = function(includeInstance, msg) { - var f, obj = { - locationIdList: (f = jspb.Message.getRepeatedField(msg, 1)) == null ? undefined : f, - valueList: (f = jspb.Message.getRepeatedField(msg, 2)) == null ? undefined : f, - labelList: jspb.Message.toObjectList(msg.getLabelList(), - proto.perftools.profiles.Label.toObject, includeInstance) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.perftools.profiles.Sample} - */ -proto.perftools.profiles.Sample.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.perftools.profiles.Sample; - return proto.perftools.profiles.Sample.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.perftools.profiles.Sample} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.perftools.profiles.Sample} - */ -proto.perftools.profiles.Sample.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var values = /** @type {!Array<number>} */ (reader.isDelimited() ? reader.readPackedUint64() : [reader.readUint64()]); - for (var i = 0; i < values.length; i++) { - msg.addLocationId(values[i]); - } - break; - case 2: - var values = /** @type {!Array<number>} */ (reader.isDelimited() ? reader.readPackedInt64() : [reader.readInt64()]); - for (var i = 0; i < values.length; i++) { - msg.addValue(values[i]); - } - break; - case 3: - var value = new proto.perftools.profiles.Label; - reader.readMessage(value,proto.perftools.profiles.Label.deserializeBinaryFromReader); - msg.addLabel(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.perftools.profiles.Sample.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.perftools.profiles.Sample.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.perftools.profiles.Sample} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.perftools.profiles.Sample.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getLocationIdList(); - if (f.length > 0) { - writer.writePackedUint64( - 1, - f - ); - } - f = message.getValueList(); - if (f.length > 0) { - writer.writePackedInt64( - 2, - f - ); - } - f = message.getLabelList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 3, - f, - proto.perftools.profiles.Label.serializeBinaryToWriter - ); - } -}; - - -/** - * repeated uint64 location_id = 1; - * @return {!Array<number>} - */ -proto.perftools.profiles.Sample.prototype.getLocationIdList = function() { - return /** @type {!Array<number>} */ (jspb.Message.getRepeatedField(this, 1)); -}; - - -/** - * @param {!Array<number>} value - * @return {!proto.perftools.profiles.Sample} returns this - */ -proto.perftools.profiles.Sample.prototype.setLocationIdList = function(value) { - return jspb.Message.setField(this, 1, value || []); -}; - - -/** - * @param {number} value - * @param {number=} opt_index - * @return {!proto.perftools.profiles.Sample} returns this - */ -proto.perftools.profiles.Sample.prototype.addLocationId = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 1, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.perftools.profiles.Sample} returns this - */ -proto.perftools.profiles.Sample.prototype.clearLocationIdList = function() { - return this.setLocationIdList([]); -}; - - -/** - * repeated int64 value = 2; - * @return {!Array<number>} - */ -proto.perftools.profiles.Sample.prototype.getValueList = function() { - return /** @type {!Array<number>} */ (jspb.Message.getRepeatedField(this, 2)); -}; - - -/** - * @param {!Array<number>} value - * @return {!proto.perftools.profiles.Sample} returns this - */ -proto.perftools.profiles.Sample.prototype.setValueList = function(value) { - return jspb.Message.setField(this, 2, value || []); -}; - - -/** - * @param {number} value - * @param {number=} opt_index - * @return {!proto.perftools.profiles.Sample} returns this - */ -proto.perftools.profiles.Sample.prototype.addValue = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 2, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.perftools.profiles.Sample} returns this - */ -proto.perftools.profiles.Sample.prototype.clearValueList = function() { - return this.setValueList([]); -}; - - -/** - * repeated Label label = 3; - * @return {!Array<!proto.perftools.profiles.Label>} - */ -proto.perftools.profiles.Sample.prototype.getLabelList = function() { - return /** @type{!Array<!proto.perftools.profiles.Label>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.perftools.profiles.Label, 3)); -}; - - -/** - * @param {!Array<!proto.perftools.profiles.Label>} value - * @return {!proto.perftools.profiles.Sample} returns this -*/ -proto.perftools.profiles.Sample.prototype.setLabelList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 3, value); -}; - - -/** - * @param {!proto.perftools.profiles.Label=} opt_value - * @param {number=} opt_index - * @return {!proto.perftools.profiles.Label} - */ -proto.perftools.profiles.Sample.prototype.addLabel = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 3, opt_value, proto.perftools.profiles.Label, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.perftools.profiles.Sample} returns this - */ -proto.perftools.profiles.Sample.prototype.clearLabelList = function() { - return this.setLabelList([]); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.perftools.profiles.Label.prototype.toObject = function(opt_includeInstance) { - return proto.perftools.profiles.Label.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.perftools.profiles.Label} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.perftools.profiles.Label.toObject = function(includeInstance, msg) { - var f, obj = { - key: jspb.Message.getFieldWithDefault(msg, 1, 0), - str: jspb.Message.getFieldWithDefault(msg, 2, 0), - num: jspb.Message.getFieldWithDefault(msg, 3, 0), - numUnit: jspb.Message.getFieldWithDefault(msg, 4, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.perftools.profiles.Label} - */ -proto.perftools.profiles.Label.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.perftools.profiles.Label; - return proto.perftools.profiles.Label.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.perftools.profiles.Label} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.perftools.profiles.Label} - */ -proto.perftools.profiles.Label.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt64()); - msg.setKey(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt64()); - msg.setStr(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt64()); - msg.setNum(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt64()); - msg.setNumUnit(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.perftools.profiles.Label.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.perftools.profiles.Label.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.perftools.profiles.Label} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.perftools.profiles.Label.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getKey(); - if (f !== 0) { - writer.writeInt64( - 1, - f - ); - } - f = message.getStr(); - if (f !== 0) { - writer.writeInt64( - 2, - f - ); - } - f = message.getNum(); - if (f !== 0) { - writer.writeInt64( - 3, - f - ); - } - f = message.getNumUnit(); - if (f !== 0) { - writer.writeInt64( - 4, - f - ); - } -}; - - -/** - * optional int64 key = 1; - * @return {number} - */ -proto.perftools.profiles.Label.prototype.getKey = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Label} returns this - */ -proto.perftools.profiles.Label.prototype.setKey = function(value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - - -/** - * optional int64 str = 2; - * @return {number} - */ -proto.perftools.profiles.Label.prototype.getStr = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Label} returns this - */ -proto.perftools.profiles.Label.prototype.setStr = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - -/** - * optional int64 num = 3; - * @return {number} - */ -proto.perftools.profiles.Label.prototype.getNum = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Label} returns this - */ -proto.perftools.profiles.Label.prototype.setNum = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional int64 num_unit = 4; - * @return {number} - */ -proto.perftools.profiles.Label.prototype.getNumUnit = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Label} returns this - */ -proto.perftools.profiles.Label.prototype.setNumUnit = function(value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.perftools.profiles.Mapping.prototype.toObject = function(opt_includeInstance) { - return proto.perftools.profiles.Mapping.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.perftools.profiles.Mapping} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.perftools.profiles.Mapping.toObject = function(includeInstance, msg) { - var f, obj = { - id: jspb.Message.getFieldWithDefault(msg, 1, 0), - memoryStart: jspb.Message.getFieldWithDefault(msg, 2, 0), - memoryLimit: jspb.Message.getFieldWithDefault(msg, 3, 0), - fileOffset: jspb.Message.getFieldWithDefault(msg, 4, 0), - filename: jspb.Message.getFieldWithDefault(msg, 5, 0), - buildId: jspb.Message.getFieldWithDefault(msg, 6, 0), - hasFunctions: jspb.Message.getBooleanFieldWithDefault(msg, 7, false), - hasFilenames: jspb.Message.getBooleanFieldWithDefault(msg, 8, false), - hasLineNumbers: jspb.Message.getBooleanFieldWithDefault(msg, 9, false), - hasInlineFrames: jspb.Message.getBooleanFieldWithDefault(msg, 10, false) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.perftools.profiles.Mapping} - */ -proto.perftools.profiles.Mapping.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.perftools.profiles.Mapping; - return proto.perftools.profiles.Mapping.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.perftools.profiles.Mapping} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.perftools.profiles.Mapping} - */ -proto.perftools.profiles.Mapping.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readUint64()); - msg.setId(value); - break; - case 2: - var value = /** @type {number} */ (reader.readUint64()); - msg.setMemoryStart(value); - break; - case 3: - var value = /** @type {number} */ (reader.readUint64()); - msg.setMemoryLimit(value); - break; - case 4: - var value = /** @type {number} */ (reader.readUint64()); - msg.setFileOffset(value); - break; - case 5: - var value = /** @type {number} */ (reader.readInt64()); - msg.setFilename(value); - break; - case 6: - var value = /** @type {number} */ (reader.readInt64()); - msg.setBuildId(value); - break; - case 7: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setHasFunctions(value); - break; - case 8: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setHasFilenames(value); - break; - case 9: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setHasLineNumbers(value); - break; - case 10: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setHasInlineFrames(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.perftools.profiles.Mapping.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.perftools.profiles.Mapping.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.perftools.profiles.Mapping} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.perftools.profiles.Mapping.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getId(); - if (f !== 0) { - writer.writeUint64( - 1, - f - ); - } - f = message.getMemoryStart(); - if (f !== 0) { - writer.writeUint64( - 2, - f - ); - } - f = message.getMemoryLimit(); - if (f !== 0) { - writer.writeUint64( - 3, - f - ); - } - f = message.getFileOffset(); - if (f !== 0) { - writer.writeUint64( - 4, - f - ); - } - f = message.getFilename(); - if (f !== 0) { - writer.writeInt64( - 5, - f - ); - } - f = message.getBuildId(); - if (f !== 0) { - writer.writeInt64( - 6, - f - ); - } - f = message.getHasFunctions(); - if (f) { - writer.writeBool( - 7, - f - ); - } - f = message.getHasFilenames(); - if (f) { - writer.writeBool( - 8, - f - ); - } - f = message.getHasLineNumbers(); - if (f) { - writer.writeBool( - 9, - f - ); - } - f = message.getHasInlineFrames(); - if (f) { - writer.writeBool( - 10, - f - ); - } -}; - - -/** - * optional uint64 id = 1; - * @return {number} - */ -proto.perftools.profiles.Mapping.prototype.getId = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Mapping} returns this - */ -proto.perftools.profiles.Mapping.prototype.setId = function(value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - - -/** - * optional uint64 memory_start = 2; - * @return {number} - */ -proto.perftools.profiles.Mapping.prototype.getMemoryStart = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Mapping} returns this - */ -proto.perftools.profiles.Mapping.prototype.setMemoryStart = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - -/** - * optional uint64 memory_limit = 3; - * @return {number} - */ -proto.perftools.profiles.Mapping.prototype.getMemoryLimit = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Mapping} returns this - */ -proto.perftools.profiles.Mapping.prototype.setMemoryLimit = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional uint64 file_offset = 4; - * @return {number} - */ -proto.perftools.profiles.Mapping.prototype.getFileOffset = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Mapping} returns this - */ -proto.perftools.profiles.Mapping.prototype.setFileOffset = function(value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - - -/** - * optional int64 filename = 5; - * @return {number} - */ -proto.perftools.profiles.Mapping.prototype.getFilename = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Mapping} returns this - */ -proto.perftools.profiles.Mapping.prototype.setFilename = function(value) { - return jspb.Message.setProto3IntField(this, 5, value); -}; - - -/** - * optional int64 build_id = 6; - * @return {number} - */ -proto.perftools.profiles.Mapping.prototype.getBuildId = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Mapping} returns this - */ -proto.perftools.profiles.Mapping.prototype.setBuildId = function(value) { - return jspb.Message.setProto3IntField(this, 6, value); -}; - - -/** - * optional bool has_functions = 7; - * @return {boolean} - */ -proto.perftools.profiles.Mapping.prototype.getHasFunctions = function() { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 7, false)); -}; - - -/** - * @param {boolean} value - * @return {!proto.perftools.profiles.Mapping} returns this - */ -proto.perftools.profiles.Mapping.prototype.setHasFunctions = function(value) { - return jspb.Message.setProto3BooleanField(this, 7, value); -}; - - -/** - * optional bool has_filenames = 8; - * @return {boolean} - */ -proto.perftools.profiles.Mapping.prototype.getHasFilenames = function() { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 8, false)); -}; - - -/** - * @param {boolean} value - * @return {!proto.perftools.profiles.Mapping} returns this - */ -proto.perftools.profiles.Mapping.prototype.setHasFilenames = function(value) { - return jspb.Message.setProto3BooleanField(this, 8, value); -}; - - -/** - * optional bool has_line_numbers = 9; - * @return {boolean} - */ -proto.perftools.profiles.Mapping.prototype.getHasLineNumbers = function() { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 9, false)); -}; - - -/** - * @param {boolean} value - * @return {!proto.perftools.profiles.Mapping} returns this - */ -proto.perftools.profiles.Mapping.prototype.setHasLineNumbers = function(value) { - return jspb.Message.setProto3BooleanField(this, 9, value); -}; - - -/** - * optional bool has_inline_frames = 10; - * @return {boolean} - */ -proto.perftools.profiles.Mapping.prototype.getHasInlineFrames = function() { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 10, false)); -}; - - -/** - * @param {boolean} value - * @return {!proto.perftools.profiles.Mapping} returns this - */ -proto.perftools.profiles.Mapping.prototype.setHasInlineFrames = function(value) { - return jspb.Message.setProto3BooleanField(this, 10, value); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.perftools.profiles.Location.repeatedFields_ = [4]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.perftools.profiles.Location.prototype.toObject = function(opt_includeInstance) { - return proto.perftools.profiles.Location.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.perftools.profiles.Location} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.perftools.profiles.Location.toObject = function(includeInstance, msg) { - var f, obj = { - id: jspb.Message.getFieldWithDefault(msg, 1, 0), - mappingId: jspb.Message.getFieldWithDefault(msg, 2, 0), - address: jspb.Message.getFieldWithDefault(msg, 3, 0), - lineList: jspb.Message.toObjectList(msg.getLineList(), - proto.perftools.profiles.Line.toObject, includeInstance), - isFolded: jspb.Message.getBooleanFieldWithDefault(msg, 5, false) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.perftools.profiles.Location} - */ -proto.perftools.profiles.Location.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.perftools.profiles.Location; - return proto.perftools.profiles.Location.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.perftools.profiles.Location} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.perftools.profiles.Location} - */ -proto.perftools.profiles.Location.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readUint64()); - msg.setId(value); - break; - case 2: - var value = /** @type {number} */ (reader.readUint64()); - msg.setMappingId(value); - break; - case 3: - var value = /** @type {number} */ (reader.readUint64()); - msg.setAddress(value); - break; - case 4: - var value = new proto.perftools.profiles.Line; - reader.readMessage(value,proto.perftools.profiles.Line.deserializeBinaryFromReader); - msg.addLine(value); - break; - case 5: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setIsFolded(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.perftools.profiles.Location.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.perftools.profiles.Location.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.perftools.profiles.Location} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.perftools.profiles.Location.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getId(); - if (f !== 0) { - writer.writeUint64( - 1, - f - ); - } - f = message.getMappingId(); - if (f !== 0) { - writer.writeUint64( - 2, - f - ); - } - f = message.getAddress(); - if (f !== 0) { - writer.writeUint64( - 3, - f - ); - } - f = message.getLineList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 4, - f, - proto.perftools.profiles.Line.serializeBinaryToWriter - ); - } - f = message.getIsFolded(); - if (f) { - writer.writeBool( - 5, - f - ); - } -}; - - -/** - * optional uint64 id = 1; - * @return {number} - */ -proto.perftools.profiles.Location.prototype.getId = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Location} returns this - */ -proto.perftools.profiles.Location.prototype.setId = function(value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - - -/** - * optional uint64 mapping_id = 2; - * @return {number} - */ -proto.perftools.profiles.Location.prototype.getMappingId = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Location} returns this - */ -proto.perftools.profiles.Location.prototype.setMappingId = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - -/** - * optional uint64 address = 3; - * @return {number} - */ -proto.perftools.profiles.Location.prototype.getAddress = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Location} returns this - */ -proto.perftools.profiles.Location.prototype.setAddress = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * repeated Line line = 4; - * @return {!Array<!proto.perftools.profiles.Line>} - */ -proto.perftools.profiles.Location.prototype.getLineList = function() { - return /** @type{!Array<!proto.perftools.profiles.Line>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.perftools.profiles.Line, 4)); -}; - - -/** - * @param {!Array<!proto.perftools.profiles.Line>} value - * @return {!proto.perftools.profiles.Location} returns this -*/ -proto.perftools.profiles.Location.prototype.setLineList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 4, value); -}; - - -/** - * @param {!proto.perftools.profiles.Line=} opt_value - * @param {number=} opt_index - * @return {!proto.perftools.profiles.Line} - */ -proto.perftools.profiles.Location.prototype.addLine = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 4, opt_value, proto.perftools.profiles.Line, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.perftools.profiles.Location} returns this - */ -proto.perftools.profiles.Location.prototype.clearLineList = function() { - return this.setLineList([]); -}; - - -/** - * optional bool is_folded = 5; - * @return {boolean} - */ -proto.perftools.profiles.Location.prototype.getIsFolded = function() { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 5, false)); -}; - - -/** - * @param {boolean} value - * @return {!proto.perftools.profiles.Location} returns this - */ -proto.perftools.profiles.Location.prototype.setIsFolded = function(value) { - return jspb.Message.setProto3BooleanField(this, 5, value); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.perftools.profiles.Line.prototype.toObject = function(opt_includeInstance) { - return proto.perftools.profiles.Line.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.perftools.profiles.Line} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.perftools.profiles.Line.toObject = function(includeInstance, msg) { - var f, obj = { - functionId: jspb.Message.getFieldWithDefault(msg, 1, 0), - line: jspb.Message.getFieldWithDefault(msg, 2, 0), - column: jspb.Message.getFieldWithDefault(msg, 3, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.perftools.profiles.Line} - */ -proto.perftools.profiles.Line.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.perftools.profiles.Line; - return proto.perftools.profiles.Line.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.perftools.profiles.Line} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.perftools.profiles.Line} - */ -proto.perftools.profiles.Line.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readUint64()); - msg.setFunctionId(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt64()); - msg.setLine(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt64()); - msg.setColumn(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.perftools.profiles.Line.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.perftools.profiles.Line.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.perftools.profiles.Line} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.perftools.profiles.Line.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getFunctionId(); - if (f !== 0) { - writer.writeUint64( - 1, - f - ); - } - f = message.getLine(); - if (f !== 0) { - writer.writeInt64( - 2, - f - ); - } - f = message.getColumn(); - if (f !== 0) { - writer.writeInt64( - 3, - f - ); - } -}; - - -/** - * optional uint64 function_id = 1; - * @return {number} - */ -proto.perftools.profiles.Line.prototype.getFunctionId = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Line} returns this - */ -proto.perftools.profiles.Line.prototype.setFunctionId = function(value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - - -/** - * optional int64 line = 2; - * @return {number} - */ -proto.perftools.profiles.Line.prototype.getLine = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Line} returns this - */ -proto.perftools.profiles.Line.prototype.setLine = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - -/** - * optional int64 column = 3; - * @return {number} - */ -proto.perftools.profiles.Line.prototype.getColumn = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Line} returns this - */ -proto.perftools.profiles.Line.prototype.setColumn = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.perftools.profiles.Function.prototype.toObject = function(opt_includeInstance) { - return proto.perftools.profiles.Function.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.perftools.profiles.Function} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.perftools.profiles.Function.toObject = function(includeInstance, msg) { - var f, obj = { - id: jspb.Message.getFieldWithDefault(msg, 1, 0), - name: jspb.Message.getFieldWithDefault(msg, 2, 0), - systemName: jspb.Message.getFieldWithDefault(msg, 3, 0), - filename: jspb.Message.getFieldWithDefault(msg, 4, 0), - startLine: jspb.Message.getFieldWithDefault(msg, 5, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.perftools.profiles.Function} - */ -proto.perftools.profiles.Function.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.perftools.profiles.Function; - return proto.perftools.profiles.Function.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.perftools.profiles.Function} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.perftools.profiles.Function} - */ -proto.perftools.profiles.Function.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readUint64()); - msg.setId(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt64()); - msg.setName(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt64()); - msg.setSystemName(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt64()); - msg.setFilename(value); - break; - case 5: - var value = /** @type {number} */ (reader.readInt64()); - msg.setStartLine(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.perftools.profiles.Function.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.perftools.profiles.Function.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.perftools.profiles.Function} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.perftools.profiles.Function.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getId(); - if (f !== 0) { - writer.writeUint64( - 1, - f - ); - } - f = message.getName(); - if (f !== 0) { - writer.writeInt64( - 2, - f - ); - } - f = message.getSystemName(); - if (f !== 0) { - writer.writeInt64( - 3, - f - ); - } - f = message.getFilename(); - if (f !== 0) { - writer.writeInt64( - 4, - f - ); - } - f = message.getStartLine(); - if (f !== 0) { - writer.writeInt64( - 5, - f - ); - } -}; - - -/** - * optional uint64 id = 1; - * @return {number} - */ -proto.perftools.profiles.Function.prototype.getId = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Function} returns this - */ -proto.perftools.profiles.Function.prototype.setId = function(value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - - -/** - * optional int64 name = 2; - * @return {number} - */ -proto.perftools.profiles.Function.prototype.getName = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Function} returns this - */ -proto.perftools.profiles.Function.prototype.setName = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - -/** - * optional int64 system_name = 3; - * @return {number} - */ -proto.perftools.profiles.Function.prototype.getSystemName = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Function} returns this - */ -proto.perftools.profiles.Function.prototype.setSystemName = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional int64 filename = 4; - * @return {number} - */ -proto.perftools.profiles.Function.prototype.getFilename = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Function} returns this - */ -proto.perftools.profiles.Function.prototype.setFilename = function(value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - - -/** - * optional int64 start_line = 5; - * @return {number} - */ -proto.perftools.profiles.Function.prototype.getStartLine = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.perftools.profiles.Function} returns this - */ -proto.perftools.profiles.Function.prototype.setStartLine = function(value) { - return jspb.Message.setProto3IntField(this, 5, value); -}; - - -goog.object.extend(exports, proto.perftools.profiles); diff --git a/pyroscope/pyroscope.js b/pyroscope/pyroscope.js deleted file mode 100644 index e2788b1b..00000000 --- a/pyroscope/pyroscope.js +++ /dev/null @@ -1,655 +0,0 @@ -const messages = require('./querier_pb') -const types = require('./types/v1/types_pb') -const services = require('./querier_grpc_pb') -const clickhouse = require('../lib/db/clickhouse') -const { DATABASE_NAME } = require('../lib/utils') -const Sql = require('@cloki/clickhouse-sql') -const pprofBin = require('./pprof-bin/pkg/pprof_bin') -const { QrynBadRequest } = require('../lib/handlers/errors') -const { clusterName } = require('../common') -const logger = require('../lib/logger') -const jsonParsers = require('./json_parsers') -const renderDiff = require('./render_diff') -const { - parser, - wrapResponse, - parseTypeId, - serviceNameSelectorQuery, - labelSelectorQuery, - HISTORY_TIMESPAN -} = require('./shared') -const settings = require('./settings') -const { mergeStackTraces, newCtxIdx } = require('./merge_stack_traces') -const { selectSeriesImpl } = require('./select_series') -const render = require('./render') - -const profileTypesHandler = async (req, res) => { - const dist = clusterName ? '_dist' : '' - const _res = new messages.ProfileTypesResponse() - const fromTimeSec = Math.floor(req.body && req.body.getStart - ? parseInt(req.body.getStart()) / 1000 - : (Date.now() - HISTORY_TIMESPAN) / 1000) - const toTimeSec = Math.floor(req.body && req.body.getEnd - ? parseInt(req.body.getEnd()) / 1000 - : Date.now() / 1000) - const profileTypes = await clickhouse.rawRequest(`SELECT DISTINCT type_id, sample_type_unit -FROM profiles_series${dist} ARRAY JOIN sample_types_units as sample_type_unit -WHERE date >= toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)})) AND date <= toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)})) FORMAT JSON`, - null, DATABASE_NAME()) - _res.setProfileTypesList(profileTypes.data.data.map(profileType => { - const pt = new types.ProfileType() - const [name, periodType, periodUnit] = profileType.type_id.split(':') - const typeIdParts = profileType.type_id.match(/^([^:]+):(.*)$/) - pt.setId(typeIdParts[1] + ':' + profileType.sample_type_unit[0] + ':' + profileType.sample_type_unit[1] + - ':' + typeIdParts[2]) - pt.setName(name) - pt.setSampleType(profileType.sample_type_unit[0]) - pt.setSampleUnit(profileType.sample_type_unit[1]) - pt.setPeriodType(periodType) - pt.setPeriodUnit(periodUnit) - return pt - })) - return _res -} - -const labelNames = async (req, res) => { - const body = req.body - const dist = clusterName ? '_dist' : '' - const fromTimeSec = Math.floor(req.body && req.body.getStart - ? parseInt(req.body.getStart()) / 1000 - : (Date.now() - HISTORY_TIMESPAN) / 1000) - const toTimeSec = Math.floor(req.body && req.body.getEnd - ? parseInt(req.body.getEnd()) / 1000 - : Date.now() / 1000) - if (!body.getMatchersList || body.getMatchersList().length === 0) { - const q = `SELECT DISTINCT key - FROM profiles_series_keys ${dist} - WHERE date >= toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)})) - AND date <= toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)})) FORMAT JSON` - console.log(q) - const labelNames = await clickhouse.rawRequest(q, null, DATABASE_NAME()) - const resp = new types.LabelNamesResponse() - resp.setNamesList(labelNames.data.data.map(label => label.key)) - return resp - } - const promises = [] - for (const matcher of body.getMatchersList()) { - const specialMatchers = getSpecialMatchers(matcher) - const idxReq = matcherIdxRequest(matcher, specialMatchers, fromTimeSec, toTimeSec) - const withIdxReq = new Sql.With('idx', idxReq) - const specialClauses = specialMatchersQuery(specialMatchers.matchers, - 'sample_types_units') - const serviceNameSelector = serviceNameSelectorQuery(matcher) - const req = (new Sql.Select()).with(withIdxReq) - .select('key') - .distinct(true) - .from(`profiles_series_gin${dist}`) - .where(Sql.And( - specialClauses, - serviceNameSelector, - Sql.Gte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)}))`)), - Sql.Lte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)}))`)), - new Sql.In('fingerprint', 'IN', new Sql.WithReference(withIdxReq)) - )) - promises.push(clickhouse.rawRequest(req.toString() + ' FORMAT JSON', null, DATABASE_NAME())) - } - const labelNames = await Promise.all(promises) - const labelNamesDedup = Object.fromEntries( - labelNames.flatMap(val => { - return val.data.data.map(row => [row.key, true]) - }) - ) - const resp = new types.LabelNamesResponse() - resp.setNamesList([...Object.keys(labelNamesDedup)]) - return resp -} - -const labelValues = async (req, res) => { - const dist = clusterName ? '_dist' : '' - const body = req.body; - const name = req.body && req.body.getName - ? req.body.getName() - : '' - const fromTimeSec = Math.floor(req.body && req.body.getStart && req.body.getStart() - ? parseInt(req.body.getStart()) / 1000 - : (Date.now() - HISTORY_TIMESPAN) / 1000) - const toTimeSec = Math.floor(req.body && req.body.getEnd && req.body.getEnd() - ? parseInt(req.body.getEnd()) / 1000 - : Date.now() / 1000) - if (!name) { - throw new Error('No name provided') - } - if (!body.getMatchersList || body.getMatchersList().length === 0) { - const labelValues = await clickhouse.rawRequest(`SELECT DISTINCT val -FROM profiles_series_gin${dist} -WHERE key = ${Sql.quoteVal(name)} AND -date >= toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)})) AND -date <= toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)})) FORMAT JSON`, null, DATABASE_NAME()) - const resp = new types.LabelValuesResponse() - resp.setNamesList(labelValues.data.data.map(label => label.val)) - return resp - } - const promises = [] - for (const matcher of body.getMatchersList()) { - const specialMatchers = getSpecialMatchers(matcher) - const idxReq = matcherIdxRequest(matcher, specialMatchers, fromTimeSec, toTimeSec) - const withIdxReq = new Sql.With('idx', idxReq) - const specialClauses = specialMatchersQuery(specialMatchers.matchers, - 'sample_types_units') - const serviceNameSelector = serviceNameSelectorQuery(matcher) - const req = (new Sql.Select()).with(withIdxReq) - .select('val') - .distinct(true) - .from(`profiles_series_gin${dist}`) - .where(Sql.And( - specialClauses, - serviceNameSelector, - Sql.Gte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)}))`)), - Sql.Lte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)}))`)), - Sql.Eq('key', name), - new Sql.In('fingerprint', 'IN', new Sql.WithReference(withIdxReq)) - )) - console.log(req.toString()) - promises.push(clickhouse.rawRequest(req.toString() + ' FORMAT JSON', null, DATABASE_NAME())) - } - const labelValues = await Promise.all(promises) - const labelValuesDedup = Object.fromEntries( - labelValues.flatMap(val => val.data.data.map(row => [row.val, true])) - ) - const resp = new types.LabelValuesResponse() - resp.setNamesList([...Object.keys(labelValuesDedup)]) - return resp -} - -const selectMergeStacktraces = async (req, res) => { - return await selectMergeStacktracesV2(req, res) -} - -const selectMergeStacktracesV2 = async (req, res) => { - const typeRegex = parseTypeId(req.body.getProfileTypeid()) - const sel = req.body.getLabelSelector() - const fromTimeSec = req.body && req.body.getStart() - ? Math.floor(parseInt(req.body.getStart()) / 1000) - : Math.floor((Date.now() - 1000 * 60 * 60 * 48) / 1000) - const toTimeSec = req.body && req.body.getEnd() - ? Math.floor(parseInt(req.body.getEnd()) / 1000) - : Math.floor(Date.now() / 1000) - const resBuffer = await mergeStackTraces(typeRegex, sel, fromTimeSec, toTimeSec, req.log) - return res.code(200).send(resBuffer) -} - -const selectSeries = async (req, res) => { - const fromTimeSec = Math.floor(req.getStart && req.getStart() - ? parseInt(req.getStart()) / 1000 - : (Date.now() - HISTORY_TIMESPAN) / 1000) - const toTimeSec = Math.floor(req.getEnd && req.getEnd() - ? parseInt(req.getEnd()) / 1000 - : Date.now() / 1000) - return selectSeriesImpl(fromTimeSec, toTimeSec, req.body) -} - -let mergeRequestsCounter = 0 -const mergeRequestsLimit = 10 - -const selectMergeProfile = async (req, res) => { - const ctx = newCtxIdx() - try { - const _req = req.body - const fromTimeSec = Math.floor(_req && _req.getStart - ? parseInt(_req.getStart()) / 1000 - : (Date.now() - HISTORY_TIMESPAN) / 1000) - const toTimeSec = Math.floor(_req && _req.getEnd - ? parseInt(_req.getEnd()) / 1000 - : Date.now() / 1000) - let typeID = _req.getProfileTypeid && _req.getProfileTypeid() - if (!typeID) { - throw new QrynBadRequest('No type provided') - } - typeID = parseTypeId(typeID) - if (!typeID) { - throw new QrynBadRequest('Invalid type provided') - } - const dist = clusterName ? '_dist' : '' - // const sampleTypeId = typeID.sampleType + ':' + typeID.sampleUnit - const labelSelector = _req.getLabelSelector && _req.getLabelSelector() - - const typeIdSelector = Sql.Eq( - 'type_id', - Sql.val(`${typeID.type}:${typeID.periodType}:${typeID.periodUnit}`)) - const serviceNameSelector = serviceNameSelectorQuery(labelSelector) - - const idxReq = (new Sql.Select()) - .select(new Sql.Raw('fingerprint')) - .from(`${DATABASE_NAME()}.profiles_series_gin`) - .where( - Sql.And( - typeIdSelector, - serviceNameSelector, - Sql.Gte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)}))`)), - Sql.Lte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)}))`)), - Sql.Eq( - new Sql.Raw( - `has(sample_types_units, (${Sql.quoteVal(typeID.sampleType)}, ${Sql.quoteVal(typeID.sampleUnit)}))` - ), - 1 - ) - ) - ) - labelSelectorQuery(idxReq, labelSelector) - const withIdxReq = (new Sql.With('idx', idxReq, !!clusterName)) - let mainReq = (new Sql.Select()) - .with(withIdxReq) - .select([new Sql.Raw('payload'), 'payload']) - .from([`${DATABASE_NAME()}.profiles${dist}`, 'p']) - .where(Sql.And( - new Sql.In('p.fingerprint', 'IN', new Sql.WithReference(withIdxReq)), - Sql.Gte('p.timestamp_ns', new Sql.Raw(`${fromTimeSec}000000000`)), - Sql.Lt('p.timestamp_ns', new Sql.Raw(`${toTimeSec}000000000`)))) - .orderBy([new Sql.Raw('timestamp_ns'), 'DESC'], [new Sql.Raw('p.fingerprint'), 'ASC']) - if (process.env.ADVANCED_PROFILES_MERGE_LIMIT) { - mainReq = mainReq.limit(parseInt(process.env.ADVANCED_PROFILES_MERGE_LIMIT)) - } - const approxReq = (new Sql.Select()) - .select( - [new Sql.Raw('sum(length(payload))'), 'size'], - [new Sql.Raw('count()'), 'count'] - ) - .from([new Sql.Raw('(' + mainReq.toString() + ')'), 'main']) - const approx = await clickhouse.rawRequest( - approxReq.toString() + ' FORMAT JSON', null, DATABASE_NAME() - ) - const approxData = approx.data.data[0] - logger.debug(`Approximate size: ${approxData.size} bytes, profiles count: ${approxData.count}`) - const chunksCount = Math.max(Math.ceil(approxData.size / (50 * 1024 * 1024)), 1) - logger.debug(`Request is processed in: ${chunksCount} chunks`) - const chunkSize = Math.ceil(approxData.count / chunksCount) - const promises = [] - require('./pprof-bin/pkg/pprof_bin').init_panic_hook() - let processNs = BigInt(0) - const start = process.hrtime.bigint() - - for (let i = 0; i < chunksCount; i++) { - promises.push((async (i) => { - // eslint-disable-next-line no-unmodified-loop-condition - while (mergeRequestsCounter >= mergeRequestsLimit) { - await (new Promise((resolve) => setTimeout(resolve, 50))) - } - logger.debug(`Processing chunk ${i}`) - mergeRequestsCounter++ - let profiles = null - try { - let end = i * chunkSize + chunkSize - if (process.env.ADVANCED_PROFILES_MERGE_LIMIT && end > process.env.ADVANCED_PROFILES_MERGE_LIMIT) { - end = process.env.ADVANCED_PROFILES_MERGE_LIMIT - } - mainReq.limit(end - i * chunkSize, i * chunkSize) - profiles = await clickhouse.rawRequest(mainReq.toString() + ' FORMAT RowBinary', - null, - DATABASE_NAME(), - { - responseType: 'arraybuffer' - }) - } finally { - mergeRequestsCounter-- - } - const binData = Uint8Array.from(profiles.data) - logger.debug(`Chunk ${i} - ${binData.length} bytes`) - const start = process.hrtime.bigint() - pprofBin.merge_trees_pprof(ctx, binData) - const end = process.hrtime.bigint() - processNs += end - start - })(i)) - } - await Promise.all(promises) - const response = pprofBin.export_trees_pprof(ctx) - const end = process.hrtime.bigint() - - logger.debug(`Pprof merge took ${processNs} nanoseconds`) - logger.debug(`Pprof load + merge took ${end - start} nanoseconds`) - return res.code(200).send(Buffer.from(response)) - } finally { - pprofBin.drop_tree(ctx) - } -} - -/** - * - * @param labelSelector {string} - * @param specialMatchers {object || undefined} - * @param fromTimeSec {number} - * @param toTimeSec {number} - * @returns {Sql.Select} - */ -const matcherIdxRequest = (labelSelector, specialMatchers, fromTimeSec, toTimeSec) => { - specialMatchers = specialMatchers || getSpecialMatchers(labelSelector) - const specialClauses = specialMatchersQuery(specialMatchers.matchers, - 'sample_types_units') - const serviceNameSelector = serviceNameSelectorQuery(labelSelector) - const idxReq = (new Sql.Select()) - .select(new Sql.Raw('fingerprint')) - .from(`${DATABASE_NAME()}.profiles_series_gin`) - .where( - Sql.And( - specialClauses, - serviceNameSelector, - Sql.Gte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)}))`)), - Sql.Lte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)}))`)) - ) - ) - if (!specialMatchers.query.match(/^[{} ]*$/)) { - labelSelectorQuery(idxReq, specialMatchers.query) - } - return idxReq -} - -const series = async (req, res) => { - const _req = req.body - const fromTimeSec = Math.floor(_req.getStart && _req.getStart() - ? parseInt(_req.getStart()) / 1000 - : (Date.now() - HISTORY_TIMESPAN) / 1000) - const toTimeSec = Math.floor(_req.getEnd && _req.getEnd() - ? parseInt(_req.getEnd()) / 1000 - : Date.now() / 1000) - const dist = clusterName ? '_dist' : '' - const promises = [] - for (const labelSelector of _req.getMatchersList() || []) { - const specialMatchers = getSpecialMatchers(labelSelector) - // Special matchers -> query clauses - const sampleTypesUnitsFieldName = '_sample_types_units' - const clauses = [] - if (specialMatchers.__name__) { - clauses.push(matcherClause("splitByChar(':', type_id)[1]", specialMatchers.__name__)) - } - if (specialMatchers.__period_type__) { - clauses.push(matcherClause("splitByChar(':', type_id)[2]", specialMatchers.__period_type__)) - } - if (specialMatchers.__period_unit__) { - clauses.push(matcherClause("splitByChar(':', type_id)[3]", specialMatchers.__period_unit__)) - } - if (specialMatchers.__sample_type__) { - clauses.push(matcherClause(`${sampleTypesUnitsFieldName}.1`, specialMatchers.__sample_type__)) - } - if (specialMatchers.__sample_unit__) { - clauses.push(matcherClause(`${sampleTypesUnitsFieldName}.2`, specialMatchers.__sample_unit__)) - } - if (specialMatchers.__profile_type__) { - clauses.push(matcherClause( - `format('{}:{}:{}:{}:{}', (splitByChar(':', type_id) as _parts)[1], ${sampleTypesUnitsFieldName}.1, ${sampleTypesUnitsFieldName}.2, _parts[2], _parts[3])`, - specialMatchers.__profile_type__)) - } - let specialClauses = null - if (clauses.length === 0) { - specialClauses = Sql.Eq(new Sql.Raw('1'), 1) - } else if (clauses.length === 1) { - specialClauses = clauses[0] - } else { - specialClauses = Sql.And(...clauses) - } - // - const serviceNameSelector = serviceNameSelectorQuery(labelSelector) - const idxReq = matcherIdxRequest(labelSelector, specialMatchers, fromTimeSec, toTimeSec) - const withIdxReq = (new Sql.With('idx', idxReq, !!clusterName)) - const labelsReq = (new Sql.Select()) - .with(withIdxReq) - .select( - ['tags', 'tags'], - ['type_id', 'type_id'], - ['sample_types_units', '_sample_types_units']) - .from([`${DATABASE_NAME()}.profiles_series${dist}`, 'p']) - .join('p.sample_types_units', 'array') - .where( - Sql.And( - serviceNameSelector, - specialClauses, - Sql.Gte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)}))`)), - Sql.Lte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)}))`)), - new Sql.In('p.fingerprint', 'IN', new Sql.WithReference(withIdxReq)) - ) - ) - promises.push(clickhouse.rawRequest(labelsReq.toString() + ' FORMAT JSON', null, DATABASE_NAME())) - } - if ((_req.getMatchersList() || []).length === 0) { - const labelsReq = (new Sql.Select()) - .select( - ['tags', 'tags'], - ['type_id', 'type_id'], - ['sample_types_units', '_sample_types_units']) - .from([`${DATABASE_NAME()}.profiles_series${dist}`, 'p']) - .join('p.sample_types_units', 'array') - .where( - Sql.And( - Sql.Gte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)}))`)), - Sql.Lte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)}))`)) - ) - ) - promises.push(clickhouse.rawRequest(labelsReq.toString() + ' FORMAT JSON', null, DATABASE_NAME())) - } - const resp = await Promise.all(promises) - const response = new messages.SeriesResponse() - const labelsSet = [] - const filterLabelNames = _req.getLabelNamesList() || null - resp.forEach(_res => { - for (const row of _res.data.data) { - const labels = new types.Labels() - let _labels = [] - for (const tag of row.tags) { - const pair = new types.LabelPair() - pair.setName(tag[0]) - pair.setValue(tag[1]) - _labels.push(pair) - } - const typeId = row.type_id.split(':') - const _pair = (name, val) => { - const pair = new types.LabelPair() - pair.setName(name) - pair.setValue(val) - return pair - } - _labels.push( - _pair('__name__', typeId[0]), - _pair('__period_type__', typeId[1]), - _pair('__period_unit__', typeId[2]), - _pair('__sample_type__', row._sample_types_units[0]), - _pair('__sample_unit__', row._sample_types_units[1]), - _pair('__profile_type__', - `${typeId[0]}:${row._sample_types_units[0]}:${row._sample_types_units[1]}:${typeId[1]}:${typeId[2]}`) - ) - if (filterLabelNames && filterLabelNames.length) { - _labels = _labels.filter((l) => filterLabelNames.includes(l.getName())) - } - if (_labels.length > 0) { - labels.setLabelsList(_labels) - labelsSet.push(labels) - } - } - }) - response.setLabelsSetList(labelsSet) - return response -} - -/** - * returns special matchers and sanitized query without them as following: - * @example - * { - * "matchers": { - * "__name__": ["=", "foo"], - * "__period_type__": ["=~", "bar"], - * }, - * "query": "{service_name=\"abc\", job=\"def\"}" - * } - * - * @param query {string} - * @returns {{ - * matchers: { [fieldName: string]: [operator: string, value: string] }, - * query: string - * }} - */ -const getSpecialMatchers = (query) => { - if (query.length <= 2) { - return { - matchers: {}, - query: query - } - } - const res = {} - for (const name of - ['__name__', '__period_type__', '__period_unit__', '__sample_type__', '__sample_unit__', '__profile_type__']) { - const re = new RegExp(`${name}\\s*(=~|!~|=|!=)\\s*("([^"]|\\\\.)+"),*`, 'g') - const pair = re.exec(query) - if (pair) { - res[name] = [pair[1], JSON.parse(pair[2])] - } - query = query.replaceAll(re, '') - } - query = query.replaceAll(/,\s*}$/g, '}') - return { - matchers: res, - query: query - } -} - -const matcherClause = (field, matcher) => { - let valRul - const val = matcher[1] - switch (matcher[0]) { - case '=': - valRul = Sql.Eq(new Sql.Raw(field), Sql.val(val)) - break - case '!=': - valRul = Sql.Ne(new Sql.Raw(field), Sql.val(val)) - break - case '=~': - valRul = Sql.Eq(new Sql.Raw(`match(${(new Sql.Raw(field)).toString()}, ${Sql.quoteVal(val)})`), 1) - break - case '!~': - valRul = Sql.Ne(new Sql.Raw(`match(${(new Sql.Raw(field)).toString()}, ${Sql.quoteVal(val)})`), 1) - } - return valRul -} - -/** - * @example - * specialMatchersQuery({ - * "__name__": ["=", "foo"], - * "__period_type__": ["=~", "bar"], - * }) - * - * @param specialMatchers {Object} - * @returns {Sql.Condition} - */ -const specialMatchersQuery = (specialMatchers) => { - const sampleTypesUnitsFieldName = 'sample_types_units' - const clauses = [] - if (specialMatchers.__name__) { - clauses.push(matcherClause("splitByChar(':', type_id)[1]", specialMatchers.__name__)) - } - if (specialMatchers.__period_type__) { - clauses.push(matcherClause("splitByChar(':', type_id)[2]", specialMatchers.__period_type__)) - } - if (specialMatchers.__period_unit__) { - clauses.push(matcherClause("splitByChar(':', type_id)[3]", specialMatchers.__period_unit__)) - } - const arrayExists = (field) => { - const arrayExists = Sql.Condition(null, null, null) - arrayExists.toString = () => { - return `arrayExists(x -> ${field}, ${sampleTypesUnitsFieldName})` - } - return arrayExists - } - if (specialMatchers.__sample_type__) { - clauses.push(arrayExists(matcherClause('x.1', specialMatchers.__sample_type__))) - } - if (specialMatchers.__sample_unit__) { - clauses.push(arrayExists(matcherClause('x.2', specialMatchers.__sample_unit__))) - } - if (specialMatchers.__profile_type__) { - clauses.push(arrayExists(matcherClause( - "format('{}:{}:{}:{}:{}', (splitByChar(':', type_id) as _parts)[1], x.1, x.2, _parts[2], _parts[3])", - specialMatchers.__profile_type__))) - } - if (clauses.length === 0) { - return Sql.Eq(new Sql.Raw('1'), 1) - } - if (clauses.length === 1) { - return clauses[0] - } - return new Sql.And(...clauses) -} - -const getProfileStats = async (req, res) => { - const sql = ` -with non_empty as (select any(1) as non_empty from profiles limit 1), - min_date as (select min(date) as min_date, max(date) as max_date from profiles_series), - min_time as ( - select intDiv(min(timestamp_ns), 1000000) as min_time, - intDiv(max(timestamp_ns), 1000000) as max_time - from profiles - where timestamp_ns < toUnixTimestamp((select any (min_date) from min_date) + INTERVAL '1 day') * 1000000000 OR - timestamp_ns >= toUnixTimestamp((select any(max_date) from min_date)) * 1000000000 - ) -select - (select any(non_empty) from non_empty) as non_empty, - (select any(min_time) from min_time) as min_time, - (select any(max_time) from min_time) as max_time -` - const sqlRes = await clickhouse.rawRequest(sql + ' FORMAT JSON', null, DATABASE_NAME()) - const response = new types.GetProfileStatsResponse() - response.setDataIngested(!!sqlRes.data.data[0].non_empty) - response.setOldestProfileTime(sqlRes.data.data[0].min_time) - response.setNewestProfileTime(sqlRes.data.data[0].max_time) - return response -} - -const analyzeQuery = async (req, res) => { - const query = req.body.getQuery() - const fromTimeSec = Math.floor(req.getStart && req.getStart() - ? parseInt(req.getStart()) / 1000 - : (Date.now() - HISTORY_TIMESPAN) / 1000) - const toTimeSec = Math.floor(req.getEnd && req.getEnd() - ? parseInt(req.getEnd()) / 1000 - : Date.now() / 1000) - - const scope = new messages.QueryScope() - scope.setComponentType('store') - scope.setComponentCount(1) - const impact = new messages.QueryImpact() - impact.setTotalBytesInTimeRange(10 * 1024 * 1024) - impact.setTotalQueriedSeries(15) - impact.setDeduplicationNeeded(false) - const response = new messages.AnalyzeQueryResponse() - response.setQueryScopesList([scope]) - response.setQueryImpact(impact) - return response -} - -module.exports.init = (fastify) => { - const fns = { - profileTypes: profileTypesHandler, - labelNames: labelNames, - labelValues: labelValues, - selectMergeStacktraces: selectMergeStacktraces, - selectSeries: selectSeries, - selectMergeProfile: selectMergeProfile, - series: series, - getProfileStats: getProfileStats, - analyzeQuery: analyzeQuery - } - const parsers = { - series: jsonParsers.series, - getProfileStats: jsonParsers.getProfileStats, - labelNames: jsonParsers.labelNames, - labelValues: jsonParsers.labelValues, - analyzeQuery: jsonParsers.analyzeQuery - } - for (const name of Object.keys(fns)) { - fastify.post(services.QuerierServiceService[name].path, (req, res) => { - return wrapResponse(fns[name])(req, res) - }, { - 'application/json': parsers[name], - '*': parser(services.QuerierServiceService[name].requestType) - }) - } - settings.init(fastify) - render.init(fastify) - renderDiff.init(fastify) -} diff --git a/pyroscope/querier_grpc_pb.js b/pyroscope/querier_grpc_pb.js deleted file mode 100644 index 4f473c34..00000000 --- a/pyroscope/querier_grpc_pb.js +++ /dev/null @@ -1,386 +0,0 @@ -// GENERATED CODE -- DO NOT EDIT! - -'use strict'; -var grpc = require('@grpc/grpc-js'); -var querier_pb = require('./querier_pb.js'); -var google_v1_profile_pb = require('./google/v1/profile_pb.js'); -var types_v1_types_pb = require('./types/v1/types_pb.js'); - -function serialize_google_v1_Profile(arg) { - if (!(arg instanceof google_v1_profile_pb.Profile)) { - throw new Error('Expected argument of type google.v1.Profile'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_google_v1_Profile(buffer_arg) { - return google_v1_profile_pb.Profile.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_querier_v1_AnalyzeQueryRequest(arg) { - if (!(arg instanceof querier_pb.AnalyzeQueryRequest)) { - throw new Error('Expected argument of type querier.v1.AnalyzeQueryRequest'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_querier_v1_AnalyzeQueryRequest(buffer_arg) { - return querier_pb.AnalyzeQueryRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_querier_v1_AnalyzeQueryResponse(arg) { - if (!(arg instanceof querier_pb.AnalyzeQueryResponse)) { - throw new Error('Expected argument of type querier.v1.AnalyzeQueryResponse'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_querier_v1_AnalyzeQueryResponse(buffer_arg) { - return querier_pb.AnalyzeQueryResponse.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_querier_v1_DiffRequest(arg) { - if (!(arg instanceof querier_pb.DiffRequest)) { - throw new Error('Expected argument of type querier.v1.DiffRequest'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_querier_v1_DiffRequest(buffer_arg) { - return querier_pb.DiffRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_querier_v1_DiffResponse(arg) { - if (!(arg instanceof querier_pb.DiffResponse)) { - throw new Error('Expected argument of type querier.v1.DiffResponse'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_querier_v1_DiffResponse(buffer_arg) { - return querier_pb.DiffResponse.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_querier_v1_ProfileTypesRequest(arg) { - if (!(arg instanceof querier_pb.ProfileTypesRequest)) { - throw new Error('Expected argument of type querier.v1.ProfileTypesRequest'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_querier_v1_ProfileTypesRequest(buffer_arg) { - return querier_pb.ProfileTypesRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_querier_v1_ProfileTypesResponse(arg) { - if (!(arg instanceof querier_pb.ProfileTypesResponse)) { - throw new Error('Expected argument of type querier.v1.ProfileTypesResponse'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_querier_v1_ProfileTypesResponse(buffer_arg) { - return querier_pb.ProfileTypesResponse.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_querier_v1_SelectMergeProfileRequest(arg) { - if (!(arg instanceof querier_pb.SelectMergeProfileRequest)) { - throw new Error('Expected argument of type querier.v1.SelectMergeProfileRequest'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_querier_v1_SelectMergeProfileRequest(buffer_arg) { - return querier_pb.SelectMergeProfileRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_querier_v1_SelectMergeSpanProfileRequest(arg) { - if (!(arg instanceof querier_pb.SelectMergeSpanProfileRequest)) { - throw new Error('Expected argument of type querier.v1.SelectMergeSpanProfileRequest'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_querier_v1_SelectMergeSpanProfileRequest(buffer_arg) { - return querier_pb.SelectMergeSpanProfileRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_querier_v1_SelectMergeSpanProfileResponse(arg) { - if (!(arg instanceof querier_pb.SelectMergeSpanProfileResponse)) { - throw new Error('Expected argument of type querier.v1.SelectMergeSpanProfileResponse'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_querier_v1_SelectMergeSpanProfileResponse(buffer_arg) { - return querier_pb.SelectMergeSpanProfileResponse.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_querier_v1_SelectMergeStacktracesRequest(arg) { - if (!(arg instanceof querier_pb.SelectMergeStacktracesRequest)) { - throw new Error('Expected argument of type querier.v1.SelectMergeStacktracesRequest'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_querier_v1_SelectMergeStacktracesRequest(buffer_arg) { - return querier_pb.SelectMergeStacktracesRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_querier_v1_SelectMergeStacktracesResponse(arg) { - if (!(arg instanceof querier_pb.SelectMergeStacktracesResponse)) { - throw new Error('Expected argument of type querier.v1.SelectMergeStacktracesResponse'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_querier_v1_SelectMergeStacktracesResponse(buffer_arg) { - return querier_pb.SelectMergeStacktracesResponse.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_querier_v1_SelectSeriesRequest(arg) { - if (!(arg instanceof querier_pb.SelectSeriesRequest)) { - throw new Error('Expected argument of type querier.v1.SelectSeriesRequest'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_querier_v1_SelectSeriesRequest(buffer_arg) { - return querier_pb.SelectSeriesRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_querier_v1_SelectSeriesResponse(arg) { - if (!(arg instanceof querier_pb.SelectSeriesResponse)) { - throw new Error('Expected argument of type querier.v1.SelectSeriesResponse'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_querier_v1_SelectSeriesResponse(buffer_arg) { - return querier_pb.SelectSeriesResponse.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_querier_v1_SeriesRequest(arg) { - if (!(arg instanceof querier_pb.SeriesRequest)) { - throw new Error('Expected argument of type querier.v1.SeriesRequest'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_querier_v1_SeriesRequest(buffer_arg) { - return querier_pb.SeriesRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_querier_v1_SeriesResponse(arg) { - if (!(arg instanceof querier_pb.SeriesResponse)) { - throw new Error('Expected argument of type querier.v1.SeriesResponse'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_querier_v1_SeriesResponse(buffer_arg) { - return querier_pb.SeriesResponse.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_types_v1_GetProfileStatsRequest(arg) { - if (!(arg instanceof types_v1_types_pb.GetProfileStatsRequest)) { - throw new Error('Expected argument of type types.v1.GetProfileStatsRequest'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_types_v1_GetProfileStatsRequest(buffer_arg) { - return types_v1_types_pb.GetProfileStatsRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_types_v1_GetProfileStatsResponse(arg) { - if (!(arg instanceof types_v1_types_pb.GetProfileStatsResponse)) { - throw new Error('Expected argument of type types.v1.GetProfileStatsResponse'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_types_v1_GetProfileStatsResponse(buffer_arg) { - return types_v1_types_pb.GetProfileStatsResponse.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_types_v1_LabelNamesRequest(arg) { - if (!(arg instanceof types_v1_types_pb.LabelNamesRequest)) { - throw new Error('Expected argument of type types.v1.LabelNamesRequest'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_types_v1_LabelNamesRequest(buffer_arg) { - return types_v1_types_pb.LabelNamesRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_types_v1_LabelNamesResponse(arg) { - if (!(arg instanceof types_v1_types_pb.LabelNamesResponse)) { - throw new Error('Expected argument of type types.v1.LabelNamesResponse'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_types_v1_LabelNamesResponse(buffer_arg) { - return types_v1_types_pb.LabelNamesResponse.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_types_v1_LabelValuesRequest(arg) { - if (!(arg instanceof types_v1_types_pb.LabelValuesRequest)) { - throw new Error('Expected argument of type types.v1.LabelValuesRequest'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_types_v1_LabelValuesRequest(buffer_arg) { - return types_v1_types_pb.LabelValuesRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_types_v1_LabelValuesResponse(arg) { - if (!(arg instanceof types_v1_types_pb.LabelValuesResponse)) { - throw new Error('Expected argument of type types.v1.LabelValuesResponse'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_types_v1_LabelValuesResponse(buffer_arg) { - return types_v1_types_pb.LabelValuesResponse.deserializeBinary(new Uint8Array(buffer_arg)); -} - - -var QuerierServiceService = exports.QuerierServiceService = { - // ProfileType returns a list of the existing profile types. -profileTypes: { - path: '/querier.v1.QuerierService/ProfileTypes', - requestStream: false, - responseStream: false, - requestType: querier_pb.ProfileTypesRequest, - responseType: querier_pb.ProfileTypesResponse, - requestSerialize: serialize_querier_v1_ProfileTypesRequest, - requestDeserialize: deserialize_querier_v1_ProfileTypesRequest, - responseSerialize: serialize_querier_v1_ProfileTypesResponse, - responseDeserialize: deserialize_querier_v1_ProfileTypesResponse, - }, - // LabelValues returns the existing label values for the provided label names. -labelValues: { - path: '/querier.v1.QuerierService/LabelValues', - requestStream: false, - responseStream: false, - requestType: types_v1_types_pb.LabelValuesRequest, - responseType: types_v1_types_pb.LabelValuesResponse, - requestSerialize: serialize_types_v1_LabelValuesRequest, - requestDeserialize: deserialize_types_v1_LabelValuesRequest, - responseSerialize: serialize_types_v1_LabelValuesResponse, - responseDeserialize: deserialize_types_v1_LabelValuesResponse, - }, - // LabelNames returns a list of the existing label names. -labelNames: { - path: '/querier.v1.QuerierService/LabelNames', - requestStream: false, - responseStream: false, - requestType: types_v1_types_pb.LabelNamesRequest, - responseType: types_v1_types_pb.LabelNamesResponse, - requestSerialize: serialize_types_v1_LabelNamesRequest, - requestDeserialize: deserialize_types_v1_LabelNamesRequest, - responseSerialize: serialize_types_v1_LabelNamesResponse, - responseDeserialize: deserialize_types_v1_LabelNamesResponse, - }, - // Series returns profiles series matching the request. A series is a unique label set. -series: { - path: '/querier.v1.QuerierService/Series', - requestStream: false, - responseStream: false, - requestType: querier_pb.SeriesRequest, - responseType: querier_pb.SeriesResponse, - requestSerialize: serialize_querier_v1_SeriesRequest, - requestDeserialize: deserialize_querier_v1_SeriesRequest, - responseSerialize: serialize_querier_v1_SeriesResponse, - responseDeserialize: deserialize_querier_v1_SeriesResponse, - }, - // SelectMergeStacktraces returns matching profiles aggregated in a flamegraph format. It will combine samples from within the same callstack, with each element being grouped by its function name. -selectMergeStacktraces: { - path: '/querier.v1.QuerierService/SelectMergeStacktraces', - requestStream: false, - responseStream: false, - requestType: querier_pb.SelectMergeStacktracesRequest, - responseType: querier_pb.SelectMergeStacktracesResponse, - requestSerialize: serialize_querier_v1_SelectMergeStacktracesRequest, - requestDeserialize: deserialize_querier_v1_SelectMergeStacktracesRequest, - responseSerialize: serialize_querier_v1_SelectMergeStacktracesResponse, - responseDeserialize: deserialize_querier_v1_SelectMergeStacktracesResponse, - }, - // SelectMergeSpanProfile returns matching profiles aggregated in a flamegraph format. It will combine samples from within the same callstack, with each element being grouped by its function name. -selectMergeSpanProfile: { - path: '/querier.v1.QuerierService/SelectMergeSpanProfile', - requestStream: false, - responseStream: false, - requestType: querier_pb.SelectMergeSpanProfileRequest, - responseType: querier_pb.SelectMergeSpanProfileResponse, - requestSerialize: serialize_querier_v1_SelectMergeSpanProfileRequest, - requestDeserialize: deserialize_querier_v1_SelectMergeSpanProfileRequest, - responseSerialize: serialize_querier_v1_SelectMergeSpanProfileResponse, - responseDeserialize: deserialize_querier_v1_SelectMergeSpanProfileResponse, - }, - // SelectMergeProfile returns matching profiles aggregated in pprof format. It will contain all information stored (so including filenames and line number, if ingested). -selectMergeProfile: { - path: '/querier.v1.QuerierService/SelectMergeProfile', - requestStream: false, - responseStream: false, - requestType: querier_pb.SelectMergeProfileRequest, - responseType: google_v1_profile_pb.Profile, - requestSerialize: serialize_querier_v1_SelectMergeProfileRequest, - requestDeserialize: deserialize_querier_v1_SelectMergeProfileRequest, - responseSerialize: serialize_google_v1_Profile, - responseDeserialize: deserialize_google_v1_Profile, - }, - // SelectSeries returns a time series for the total sum of the requested profiles. -selectSeries: { - path: '/querier.v1.QuerierService/SelectSeries', - requestStream: false, - responseStream: false, - requestType: querier_pb.SelectSeriesRequest, - responseType: querier_pb.SelectSeriesResponse, - requestSerialize: serialize_querier_v1_SelectSeriesRequest, - requestDeserialize: deserialize_querier_v1_SelectSeriesRequest, - responseSerialize: serialize_querier_v1_SelectSeriesResponse, - responseDeserialize: deserialize_querier_v1_SelectSeriesResponse, - }, - // Diff returns a diff of two profiles -diff: { - path: '/querier.v1.QuerierService/Diff', - requestStream: false, - responseStream: false, - requestType: querier_pb.DiffRequest, - responseType: querier_pb.DiffResponse, - requestSerialize: serialize_querier_v1_DiffRequest, - requestDeserialize: deserialize_querier_v1_DiffRequest, - responseSerialize: serialize_querier_v1_DiffResponse, - responseDeserialize: deserialize_querier_v1_DiffResponse, - }, - // GetProfileStats returns profile stats for the current tenant. -getProfileStats: { - path: '/querier.v1.QuerierService/GetProfileStats', - requestStream: false, - responseStream: false, - requestType: types_v1_types_pb.GetProfileStatsRequest, - responseType: types_v1_types_pb.GetProfileStatsResponse, - requestSerialize: serialize_types_v1_GetProfileStatsRequest, - requestDeserialize: deserialize_types_v1_GetProfileStatsRequest, - responseSerialize: serialize_types_v1_GetProfileStatsResponse, - responseDeserialize: deserialize_types_v1_GetProfileStatsResponse, - }, - analyzeQuery: { - path: '/querier.v1.QuerierService/AnalyzeQuery', - requestStream: false, - responseStream: false, - requestType: querier_pb.AnalyzeQueryRequest, - responseType: querier_pb.AnalyzeQueryResponse, - requestSerialize: serialize_querier_v1_AnalyzeQueryRequest, - requestDeserialize: deserialize_querier_v1_AnalyzeQueryRequest, - responseSerialize: serialize_querier_v1_AnalyzeQueryResponse, - responseDeserialize: deserialize_querier_v1_AnalyzeQueryResponse, - }, -}; - -exports.QuerierServiceClient = grpc.makeGenericClientConstructor(QuerierServiceService); diff --git a/pyroscope/querier_pb.js b/pyroscope/querier_pb.js deleted file mode 100644 index 82334400..00000000 --- a/pyroscope/querier_pb.js +++ /dev/null @@ -1,5239 +0,0 @@ -// source: querier.proto -/** - * @fileoverview - * @enhanceable - * @suppress {missingRequire} reports error on implicit type usages. - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! -/* eslint-disable */ -// @ts-nocheck - -var jspb = require('google-protobuf'); -var goog = jspb; -var global = (function() { - if (this) { return this; } - if (typeof window !== 'undefined') { return window; } - if (typeof global !== 'undefined') { return global; } - if (typeof self !== 'undefined') { return self; } - return Function('return this')(); -}.call(null)); - -var google_v1_profile_pb = require('./google/v1/profile_pb.js'); -goog.object.extend(proto, google_v1_profile_pb); -var types_v1_types_pb = require('./types/v1/types_pb.js'); -goog.object.extend(proto, types_v1_types_pb); -goog.exportSymbol('proto.querier.v1.AnalyzeQueryRequest', null, global); -goog.exportSymbol('proto.querier.v1.AnalyzeQueryResponse', null, global); -goog.exportSymbol('proto.querier.v1.DiffRequest', null, global); -goog.exportSymbol('proto.querier.v1.DiffResponse', null, global); -goog.exportSymbol('proto.querier.v1.FlameGraph', null, global); -goog.exportSymbol('proto.querier.v1.FlameGraphDiff', null, global); -goog.exportSymbol('proto.querier.v1.Level', null, global); -goog.exportSymbol('proto.querier.v1.ProfileFormat', null, global); -goog.exportSymbol('proto.querier.v1.ProfileTypesRequest', null, global); -goog.exportSymbol('proto.querier.v1.ProfileTypesResponse', null, global); -goog.exportSymbol('proto.querier.v1.QueryImpact', null, global); -goog.exportSymbol('proto.querier.v1.QueryScope', null, global); -goog.exportSymbol('proto.querier.v1.SelectMergeProfileRequest', null, global); -goog.exportSymbol('proto.querier.v1.SelectMergeSpanProfileRequest', null, global); -goog.exportSymbol('proto.querier.v1.SelectMergeSpanProfileResponse', null, global); -goog.exportSymbol('proto.querier.v1.SelectMergeStacktracesRequest', null, global); -goog.exportSymbol('proto.querier.v1.SelectMergeStacktracesResponse', null, global); -goog.exportSymbol('proto.querier.v1.SelectSeriesRequest', null, global); -goog.exportSymbol('proto.querier.v1.SelectSeriesResponse', null, global); -goog.exportSymbol('proto.querier.v1.SeriesRequest', null, global); -goog.exportSymbol('proto.querier.v1.SeriesResponse', null, global); -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.ProfileTypesRequest = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.querier.v1.ProfileTypesRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.ProfileTypesRequest.displayName = 'proto.querier.v1.ProfileTypesRequest'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.ProfileTypesResponse = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.querier.v1.ProfileTypesResponse.repeatedFields_, null); -}; -goog.inherits(proto.querier.v1.ProfileTypesResponse, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.ProfileTypesResponse.displayName = 'proto.querier.v1.ProfileTypesResponse'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.SeriesRequest = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.querier.v1.SeriesRequest.repeatedFields_, null); -}; -goog.inherits(proto.querier.v1.SeriesRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.SeriesRequest.displayName = 'proto.querier.v1.SeriesRequest'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.SeriesResponse = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.querier.v1.SeriesResponse.repeatedFields_, null); -}; -goog.inherits(proto.querier.v1.SeriesResponse, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.SeriesResponse.displayName = 'proto.querier.v1.SeriesResponse'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.SelectMergeStacktracesRequest = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.querier.v1.SelectMergeStacktracesRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.SelectMergeStacktracesRequest.displayName = 'proto.querier.v1.SelectMergeStacktracesRequest'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.SelectMergeStacktracesResponse = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.querier.v1.SelectMergeStacktracesResponse, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.SelectMergeStacktracesResponse.displayName = 'proto.querier.v1.SelectMergeStacktracesResponse'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.SelectMergeSpanProfileRequest = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.querier.v1.SelectMergeSpanProfileRequest.repeatedFields_, null); -}; -goog.inherits(proto.querier.v1.SelectMergeSpanProfileRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.SelectMergeSpanProfileRequest.displayName = 'proto.querier.v1.SelectMergeSpanProfileRequest'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.SelectMergeSpanProfileResponse = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.querier.v1.SelectMergeSpanProfileResponse, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.SelectMergeSpanProfileResponse.displayName = 'proto.querier.v1.SelectMergeSpanProfileResponse'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.DiffRequest = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.querier.v1.DiffRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.DiffRequest.displayName = 'proto.querier.v1.DiffRequest'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.DiffResponse = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.querier.v1.DiffResponse, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.DiffResponse.displayName = 'proto.querier.v1.DiffResponse'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.FlameGraph = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.querier.v1.FlameGraph.repeatedFields_, null); -}; -goog.inherits(proto.querier.v1.FlameGraph, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.FlameGraph.displayName = 'proto.querier.v1.FlameGraph'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.FlameGraphDiff = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.querier.v1.FlameGraphDiff.repeatedFields_, null); -}; -goog.inherits(proto.querier.v1.FlameGraphDiff, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.FlameGraphDiff.displayName = 'proto.querier.v1.FlameGraphDiff'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.Level = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.querier.v1.Level.repeatedFields_, null); -}; -goog.inherits(proto.querier.v1.Level, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.Level.displayName = 'proto.querier.v1.Level'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.SelectMergeProfileRequest = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.querier.v1.SelectMergeProfileRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.SelectMergeProfileRequest.displayName = 'proto.querier.v1.SelectMergeProfileRequest'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.SelectSeriesRequest = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.querier.v1.SelectSeriesRequest.repeatedFields_, null); -}; -goog.inherits(proto.querier.v1.SelectSeriesRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.SelectSeriesRequest.displayName = 'proto.querier.v1.SelectSeriesRequest'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.SelectSeriesResponse = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.querier.v1.SelectSeriesResponse.repeatedFields_, null); -}; -goog.inherits(proto.querier.v1.SelectSeriesResponse, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.SelectSeriesResponse.displayName = 'proto.querier.v1.SelectSeriesResponse'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.AnalyzeQueryRequest = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.querier.v1.AnalyzeQueryRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.AnalyzeQueryRequest.displayName = 'proto.querier.v1.AnalyzeQueryRequest'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.AnalyzeQueryResponse = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.querier.v1.AnalyzeQueryResponse.repeatedFields_, null); -}; -goog.inherits(proto.querier.v1.AnalyzeQueryResponse, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.AnalyzeQueryResponse.displayName = 'proto.querier.v1.AnalyzeQueryResponse'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.QueryScope = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.querier.v1.QueryScope, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.QueryScope.displayName = 'proto.querier.v1.QueryScope'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.querier.v1.QueryImpact = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.querier.v1.QueryImpact, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.querier.v1.QueryImpact.displayName = 'proto.querier.v1.QueryImpact'; -} - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.ProfileTypesRequest.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.ProfileTypesRequest.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.ProfileTypesRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.ProfileTypesRequest.toObject = function(includeInstance, msg) { - var f, obj = { - start: jspb.Message.getFieldWithDefault(msg, 1, 0), - end: jspb.Message.getFieldWithDefault(msg, 2, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.ProfileTypesRequest} - */ -proto.querier.v1.ProfileTypesRequest.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.ProfileTypesRequest; - return proto.querier.v1.ProfileTypesRequest.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.ProfileTypesRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.ProfileTypesRequest} - */ -proto.querier.v1.ProfileTypesRequest.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt64()); - msg.setStart(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt64()); - msg.setEnd(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.ProfileTypesRequest.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.ProfileTypesRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.ProfileTypesRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.ProfileTypesRequest.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getStart(); - if (f !== 0) { - writer.writeInt64( - 1, - f - ); - } - f = message.getEnd(); - if (f !== 0) { - writer.writeInt64( - 2, - f - ); - } -}; - - -/** - * optional int64 start = 1; - * @return {number} - */ -proto.querier.v1.ProfileTypesRequest.prototype.getStart = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.ProfileTypesRequest} returns this - */ -proto.querier.v1.ProfileTypesRequest.prototype.setStart = function(value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - - -/** - * optional int64 end = 2; - * @return {number} - */ -proto.querier.v1.ProfileTypesRequest.prototype.getEnd = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.ProfileTypesRequest} returns this - */ -proto.querier.v1.ProfileTypesRequest.prototype.setEnd = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.querier.v1.ProfileTypesResponse.repeatedFields_ = [1]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.ProfileTypesResponse.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.ProfileTypesResponse.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.ProfileTypesResponse} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.ProfileTypesResponse.toObject = function(includeInstance, msg) { - var f, obj = { - profileTypesList: jspb.Message.toObjectList(msg.getProfileTypesList(), - types_v1_types_pb.ProfileType.toObject, includeInstance) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.ProfileTypesResponse} - */ -proto.querier.v1.ProfileTypesResponse.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.ProfileTypesResponse; - return proto.querier.v1.ProfileTypesResponse.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.ProfileTypesResponse} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.ProfileTypesResponse} - */ -proto.querier.v1.ProfileTypesResponse.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = new types_v1_types_pb.ProfileType; - reader.readMessage(value,types_v1_types_pb.ProfileType.deserializeBinaryFromReader); - msg.addProfileTypes(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.ProfileTypesResponse.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.ProfileTypesResponse.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.ProfileTypesResponse} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.ProfileTypesResponse.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getProfileTypesList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 1, - f, - types_v1_types_pb.ProfileType.serializeBinaryToWriter - ); - } -}; - - -/** - * repeated types.v1.ProfileType profile_types = 1; - * @return {!Array<!proto.types.v1.ProfileType>} - */ -proto.querier.v1.ProfileTypesResponse.prototype.getProfileTypesList = function() { - return /** @type{!Array<!proto.types.v1.ProfileType>} */ ( - jspb.Message.getRepeatedWrapperField(this, types_v1_types_pb.ProfileType, 1)); -}; - - -/** - * @param {!Array<!proto.types.v1.ProfileType>} value - * @return {!proto.querier.v1.ProfileTypesResponse} returns this -*/ -proto.querier.v1.ProfileTypesResponse.prototype.setProfileTypesList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 1, value); -}; - - -/** - * @param {!proto.types.v1.ProfileType=} opt_value - * @param {number=} opt_index - * @return {!proto.types.v1.ProfileType} - */ -proto.querier.v1.ProfileTypesResponse.prototype.addProfileTypes = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.types.v1.ProfileType, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.querier.v1.ProfileTypesResponse} returns this - */ -proto.querier.v1.ProfileTypesResponse.prototype.clearProfileTypesList = function() { - return this.setProfileTypesList([]); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.querier.v1.SeriesRequest.repeatedFields_ = [1,2]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.SeriesRequest.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.SeriesRequest.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.SeriesRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SeriesRequest.toObject = function(includeInstance, msg) { - var f, obj = { - matchersList: (f = jspb.Message.getRepeatedField(msg, 1)) == null ? undefined : f, - labelNamesList: (f = jspb.Message.getRepeatedField(msg, 2)) == null ? undefined : f, - start: jspb.Message.getFieldWithDefault(msg, 3, 0), - end: jspb.Message.getFieldWithDefault(msg, 4, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.SeriesRequest} - */ -proto.querier.v1.SeriesRequest.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.SeriesRequest; - return proto.querier.v1.SeriesRequest.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.SeriesRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.SeriesRequest} - */ -proto.querier.v1.SeriesRequest.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.addMatchers(value); - break; - case 2: - var value = /** @type {string} */ (reader.readString()); - msg.addLabelNames(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt64()); - msg.setStart(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt64()); - msg.setEnd(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.SeriesRequest.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.SeriesRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.SeriesRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SeriesRequest.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getMatchersList(); - if (f.length > 0) { - writer.writeRepeatedString( - 1, - f - ); - } - f = message.getLabelNamesList(); - if (f.length > 0) { - writer.writeRepeatedString( - 2, - f - ); - } - f = message.getStart(); - if (f !== 0) { - writer.writeInt64( - 3, - f - ); - } - f = message.getEnd(); - if (f !== 0) { - writer.writeInt64( - 4, - f - ); - } -}; - - -/** - * repeated string matchers = 1; - * @return {!Array<string>} - */ -proto.querier.v1.SeriesRequest.prototype.getMatchersList = function() { - return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 1)); -}; - - -/** - * @param {!Array<string>} value - * @return {!proto.querier.v1.SeriesRequest} returns this - */ -proto.querier.v1.SeriesRequest.prototype.setMatchersList = function(value) { - return jspb.Message.setField(this, 1, value || []); -}; - - -/** - * @param {string} value - * @param {number=} opt_index - * @return {!proto.querier.v1.SeriesRequest} returns this - */ -proto.querier.v1.SeriesRequest.prototype.addMatchers = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 1, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.querier.v1.SeriesRequest} returns this - */ -proto.querier.v1.SeriesRequest.prototype.clearMatchersList = function() { - return this.setMatchersList([]); -}; - - -/** - * repeated string label_names = 2; - * @return {!Array<string>} - */ -proto.querier.v1.SeriesRequest.prototype.getLabelNamesList = function() { - return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 2)); -}; - - -/** - * @param {!Array<string>} value - * @return {!proto.querier.v1.SeriesRequest} returns this - */ -proto.querier.v1.SeriesRequest.prototype.setLabelNamesList = function(value) { - return jspb.Message.setField(this, 2, value || []); -}; - - -/** - * @param {string} value - * @param {number=} opt_index - * @return {!proto.querier.v1.SeriesRequest} returns this - */ -proto.querier.v1.SeriesRequest.prototype.addLabelNames = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 2, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.querier.v1.SeriesRequest} returns this - */ -proto.querier.v1.SeriesRequest.prototype.clearLabelNamesList = function() { - return this.setLabelNamesList([]); -}; - - -/** - * optional int64 start = 3; - * @return {number} - */ -proto.querier.v1.SeriesRequest.prototype.getStart = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.SeriesRequest} returns this - */ -proto.querier.v1.SeriesRequest.prototype.setStart = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional int64 end = 4; - * @return {number} - */ -proto.querier.v1.SeriesRequest.prototype.getEnd = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.SeriesRequest} returns this - */ -proto.querier.v1.SeriesRequest.prototype.setEnd = function(value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.querier.v1.SeriesResponse.repeatedFields_ = [2]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.SeriesResponse.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.SeriesResponse.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.SeriesResponse} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SeriesResponse.toObject = function(includeInstance, msg) { - var f, obj = { - labelsSetList: jspb.Message.toObjectList(msg.getLabelsSetList(), - types_v1_types_pb.Labels.toObject, includeInstance) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.SeriesResponse} - */ -proto.querier.v1.SeriesResponse.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.SeriesResponse; - return proto.querier.v1.SeriesResponse.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.SeriesResponse} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.SeriesResponse} - */ -proto.querier.v1.SeriesResponse.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 2: - var value = new types_v1_types_pb.Labels; - reader.readMessage(value,types_v1_types_pb.Labels.deserializeBinaryFromReader); - msg.addLabelsSet(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.SeriesResponse.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.SeriesResponse.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.SeriesResponse} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SeriesResponse.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getLabelsSetList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 2, - f, - types_v1_types_pb.Labels.serializeBinaryToWriter - ); - } -}; - - -/** - * repeated types.v1.Labels labels_set = 2; - * @return {!Array<!proto.types.v1.Labels>} - */ -proto.querier.v1.SeriesResponse.prototype.getLabelsSetList = function() { - return /** @type{!Array<!proto.types.v1.Labels>} */ ( - jspb.Message.getRepeatedWrapperField(this, types_v1_types_pb.Labels, 2)); -}; - - -/** - * @param {!Array<!proto.types.v1.Labels>} value - * @return {!proto.querier.v1.SeriesResponse} returns this -*/ -proto.querier.v1.SeriesResponse.prototype.setLabelsSetList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 2, value); -}; - - -/** - * @param {!proto.types.v1.Labels=} opt_value - * @param {number=} opt_index - * @return {!proto.types.v1.Labels} - */ -proto.querier.v1.SeriesResponse.prototype.addLabelsSet = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 2, opt_value, proto.types.v1.Labels, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.querier.v1.SeriesResponse} returns this - */ -proto.querier.v1.SeriesResponse.prototype.clearLabelsSetList = function() { - return this.setLabelsSetList([]); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.SelectMergeStacktracesRequest.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.SelectMergeStacktracesRequest.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.SelectMergeStacktracesRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SelectMergeStacktracesRequest.toObject = function(includeInstance, msg) { - var f, obj = { - profileTypeid: jspb.Message.getFieldWithDefault(msg, 1, ""), - labelSelector: jspb.Message.getFieldWithDefault(msg, 2, ""), - start: jspb.Message.getFieldWithDefault(msg, 3, 0), - end: jspb.Message.getFieldWithDefault(msg, 4, 0), - maxNodes: jspb.Message.getFieldWithDefault(msg, 5, 0), - format: jspb.Message.getFieldWithDefault(msg, 6, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.SelectMergeStacktracesRequest} - */ -proto.querier.v1.SelectMergeStacktracesRequest.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.SelectMergeStacktracesRequest; - return proto.querier.v1.SelectMergeStacktracesRequest.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.SelectMergeStacktracesRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.SelectMergeStacktracesRequest} - */ -proto.querier.v1.SelectMergeStacktracesRequest.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setProfileTypeid(value); - break; - case 2: - var value = /** @type {string} */ (reader.readString()); - msg.setLabelSelector(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt64()); - msg.setStart(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt64()); - msg.setEnd(value); - break; - case 5: - var value = /** @type {number} */ (reader.readInt64()); - msg.setMaxNodes(value); - break; - case 6: - var value = /** @type {!proto.querier.v1.ProfileFormat} */ (reader.readEnum()); - msg.setFormat(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.SelectMergeStacktracesRequest.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.SelectMergeStacktracesRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.SelectMergeStacktracesRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SelectMergeStacktracesRequest.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getProfileTypeid(); - if (f.length > 0) { - writer.writeString( - 1, - f - ); - } - f = message.getLabelSelector(); - if (f.length > 0) { - writer.writeString( - 2, - f - ); - } - f = message.getStart(); - if (f !== 0) { - writer.writeInt64( - 3, - f - ); - } - f = message.getEnd(); - if (f !== 0) { - writer.writeInt64( - 4, - f - ); - } - f = /** @type {number} */ (jspb.Message.getField(message, 5)); - if (f != null) { - writer.writeInt64( - 5, - f - ); - } - f = message.getFormat(); - if (f !== 0.0) { - writer.writeEnum( - 6, - f - ); - } -}; - - -/** - * optional string profile_typeID = 1; - * @return {string} - */ -proto.querier.v1.SelectMergeStacktracesRequest.prototype.getProfileTypeid = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - - -/** - * @param {string} value - * @return {!proto.querier.v1.SelectMergeStacktracesRequest} returns this - */ -proto.querier.v1.SelectMergeStacktracesRequest.prototype.setProfileTypeid = function(value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - - -/** - * optional string label_selector = 2; - * @return {string} - */ -proto.querier.v1.SelectMergeStacktracesRequest.prototype.getLabelSelector = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); -}; - - -/** - * @param {string} value - * @return {!proto.querier.v1.SelectMergeStacktracesRequest} returns this - */ -proto.querier.v1.SelectMergeStacktracesRequest.prototype.setLabelSelector = function(value) { - return jspb.Message.setProto3StringField(this, 2, value); -}; - - -/** - * optional int64 start = 3; - * @return {number} - */ -proto.querier.v1.SelectMergeStacktracesRequest.prototype.getStart = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.SelectMergeStacktracesRequest} returns this - */ -proto.querier.v1.SelectMergeStacktracesRequest.prototype.setStart = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional int64 end = 4; - * @return {number} - */ -proto.querier.v1.SelectMergeStacktracesRequest.prototype.getEnd = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.SelectMergeStacktracesRequest} returns this - */ -proto.querier.v1.SelectMergeStacktracesRequest.prototype.setEnd = function(value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - - -/** - * optional int64 max_nodes = 5; - * @return {number} - */ -proto.querier.v1.SelectMergeStacktracesRequest.prototype.getMaxNodes = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.SelectMergeStacktracesRequest} returns this - */ -proto.querier.v1.SelectMergeStacktracesRequest.prototype.setMaxNodes = function(value) { - return jspb.Message.setField(this, 5, value); -}; - - -/** - * Clears the field making it undefined. - * @return {!proto.querier.v1.SelectMergeStacktracesRequest} returns this - */ -proto.querier.v1.SelectMergeStacktracesRequest.prototype.clearMaxNodes = function() { - return jspb.Message.setField(this, 5, undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.querier.v1.SelectMergeStacktracesRequest.prototype.hasMaxNodes = function() { - return jspb.Message.getField(this, 5) != null; -}; - - -/** - * optional ProfileFormat format = 6; - * @return {!proto.querier.v1.ProfileFormat} - */ -proto.querier.v1.SelectMergeStacktracesRequest.prototype.getFormat = function() { - return /** @type {!proto.querier.v1.ProfileFormat} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); -}; - - -/** - * @param {!proto.querier.v1.ProfileFormat} value - * @return {!proto.querier.v1.SelectMergeStacktracesRequest} returns this - */ -proto.querier.v1.SelectMergeStacktracesRequest.prototype.setFormat = function(value) { - return jspb.Message.setProto3EnumField(this, 6, value); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.SelectMergeStacktracesResponse.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.SelectMergeStacktracesResponse.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.SelectMergeStacktracesResponse} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SelectMergeStacktracesResponse.toObject = function(includeInstance, msg) { - var f, obj = { - flamegraph: (f = msg.getFlamegraph()) && proto.querier.v1.FlameGraph.toObject(includeInstance, f), - tree: msg.getTree_asB64() - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.SelectMergeStacktracesResponse} - */ -proto.querier.v1.SelectMergeStacktracesResponse.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.SelectMergeStacktracesResponse; - return proto.querier.v1.SelectMergeStacktracesResponse.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.SelectMergeStacktracesResponse} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.SelectMergeStacktracesResponse} - */ -proto.querier.v1.SelectMergeStacktracesResponse.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = new proto.querier.v1.FlameGraph; - reader.readMessage(value,proto.querier.v1.FlameGraph.deserializeBinaryFromReader); - msg.setFlamegraph(value); - break; - case 2: - var value = /** @type {!Uint8Array} */ (reader.readBytes()); - msg.setTree(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.SelectMergeStacktracesResponse.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.SelectMergeStacktracesResponse.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.SelectMergeStacktracesResponse} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SelectMergeStacktracesResponse.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getFlamegraph(); - if (f != null) { - writer.writeMessage( - 1, - f, - proto.querier.v1.FlameGraph.serializeBinaryToWriter - ); - } - f = message.getTree_asU8(); - if (f.length > 0) { - writer.writeBytes( - 2, - f - ); - } -}; - - -/** - * optional FlameGraph flamegraph = 1; - * @return {?proto.querier.v1.FlameGraph} - */ -proto.querier.v1.SelectMergeStacktracesResponse.prototype.getFlamegraph = function() { - return /** @type{?proto.querier.v1.FlameGraph} */ ( - jspb.Message.getWrapperField(this, proto.querier.v1.FlameGraph, 1)); -}; - - -/** - * @param {?proto.querier.v1.FlameGraph|undefined} value - * @return {!proto.querier.v1.SelectMergeStacktracesResponse} returns this -*/ -proto.querier.v1.SelectMergeStacktracesResponse.prototype.setFlamegraph = function(value) { - return jspb.Message.setWrapperField(this, 1, value); -}; - - -/** - * Clears the message field making it undefined. - * @return {!proto.querier.v1.SelectMergeStacktracesResponse} returns this - */ -proto.querier.v1.SelectMergeStacktracesResponse.prototype.clearFlamegraph = function() { - return this.setFlamegraph(undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.querier.v1.SelectMergeStacktracesResponse.prototype.hasFlamegraph = function() { - return jspb.Message.getField(this, 1) != null; -}; - - -/** - * optional bytes tree = 2; - * @return {!(string|Uint8Array)} - */ -proto.querier.v1.SelectMergeStacktracesResponse.prototype.getTree = function() { - return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 2, "")); -}; - - -/** - * optional bytes tree = 2; - * This is a type-conversion wrapper around `getTree()` - * @return {string} - */ -proto.querier.v1.SelectMergeStacktracesResponse.prototype.getTree_asB64 = function() { - return /** @type {string} */ (jspb.Message.bytesAsB64( - this.getTree())); -}; - - -/** - * optional bytes tree = 2; - * Note that Uint8Array is not supported on all browsers. - * @see http://caniuse.com/Uint8Array - * This is a type-conversion wrapper around `getTree()` - * @return {!Uint8Array} - */ -proto.querier.v1.SelectMergeStacktracesResponse.prototype.getTree_asU8 = function() { - return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( - this.getTree())); -}; - - -/** - * @param {!(string|Uint8Array)} value - * @return {!proto.querier.v1.SelectMergeStacktracesResponse} returns this - */ -proto.querier.v1.SelectMergeStacktracesResponse.prototype.setTree = function(value) { - return jspb.Message.setProto3BytesField(this, 2, value); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.querier.v1.SelectMergeSpanProfileRequest.repeatedFields_ = [3]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.SelectMergeSpanProfileRequest.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.SelectMergeSpanProfileRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SelectMergeSpanProfileRequest.toObject = function(includeInstance, msg) { - var f, obj = { - profileTypeid: jspb.Message.getFieldWithDefault(msg, 1, ""), - labelSelector: jspb.Message.getFieldWithDefault(msg, 2, ""), - spanSelectorList: (f = jspb.Message.getRepeatedField(msg, 3)) == null ? undefined : f, - start: jspb.Message.getFieldWithDefault(msg, 4, 0), - end: jspb.Message.getFieldWithDefault(msg, 5, 0), - maxNodes: jspb.Message.getFieldWithDefault(msg, 6, 0), - format: jspb.Message.getFieldWithDefault(msg, 7, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.SelectMergeSpanProfileRequest} - */ -proto.querier.v1.SelectMergeSpanProfileRequest.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.SelectMergeSpanProfileRequest; - return proto.querier.v1.SelectMergeSpanProfileRequest.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.SelectMergeSpanProfileRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.SelectMergeSpanProfileRequest} - */ -proto.querier.v1.SelectMergeSpanProfileRequest.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setProfileTypeid(value); - break; - case 2: - var value = /** @type {string} */ (reader.readString()); - msg.setLabelSelector(value); - break; - case 3: - var value = /** @type {string} */ (reader.readString()); - msg.addSpanSelector(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt64()); - msg.setStart(value); - break; - case 5: - var value = /** @type {number} */ (reader.readInt64()); - msg.setEnd(value); - break; - case 6: - var value = /** @type {number} */ (reader.readInt64()); - msg.setMaxNodes(value); - break; - case 7: - var value = /** @type {!proto.querier.v1.ProfileFormat} */ (reader.readEnum()); - msg.setFormat(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.SelectMergeSpanProfileRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.SelectMergeSpanProfileRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SelectMergeSpanProfileRequest.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getProfileTypeid(); - if (f.length > 0) { - writer.writeString( - 1, - f - ); - } - f = message.getLabelSelector(); - if (f.length > 0) { - writer.writeString( - 2, - f - ); - } - f = message.getSpanSelectorList(); - if (f.length > 0) { - writer.writeRepeatedString( - 3, - f - ); - } - f = message.getStart(); - if (f !== 0) { - writer.writeInt64( - 4, - f - ); - } - f = message.getEnd(); - if (f !== 0) { - writer.writeInt64( - 5, - f - ); - } - f = /** @type {number} */ (jspb.Message.getField(message, 6)); - if (f != null) { - writer.writeInt64( - 6, - f - ); - } - f = message.getFormat(); - if (f !== 0.0) { - writer.writeEnum( - 7, - f - ); - } -}; - - -/** - * optional string profile_typeID = 1; - * @return {string} - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.getProfileTypeid = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - - -/** - * @param {string} value - * @return {!proto.querier.v1.SelectMergeSpanProfileRequest} returns this - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.setProfileTypeid = function(value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - - -/** - * optional string label_selector = 2; - * @return {string} - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.getLabelSelector = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); -}; - - -/** - * @param {string} value - * @return {!proto.querier.v1.SelectMergeSpanProfileRequest} returns this - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.setLabelSelector = function(value) { - return jspb.Message.setProto3StringField(this, 2, value); -}; - - -/** - * repeated string span_selector = 3; - * @return {!Array<string>} - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.getSpanSelectorList = function() { - return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 3)); -}; - - -/** - * @param {!Array<string>} value - * @return {!proto.querier.v1.SelectMergeSpanProfileRequest} returns this - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.setSpanSelectorList = function(value) { - return jspb.Message.setField(this, 3, value || []); -}; - - -/** - * @param {string} value - * @param {number=} opt_index - * @return {!proto.querier.v1.SelectMergeSpanProfileRequest} returns this - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.addSpanSelector = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 3, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.querier.v1.SelectMergeSpanProfileRequest} returns this - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.clearSpanSelectorList = function() { - return this.setSpanSelectorList([]); -}; - - -/** - * optional int64 start = 4; - * @return {number} - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.getStart = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.SelectMergeSpanProfileRequest} returns this - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.setStart = function(value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - - -/** - * optional int64 end = 5; - * @return {number} - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.getEnd = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.SelectMergeSpanProfileRequest} returns this - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.setEnd = function(value) { - return jspb.Message.setProto3IntField(this, 5, value); -}; - - -/** - * optional int64 max_nodes = 6; - * @return {number} - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.getMaxNodes = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.SelectMergeSpanProfileRequest} returns this - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.setMaxNodes = function(value) { - return jspb.Message.setField(this, 6, value); -}; - - -/** - * Clears the field making it undefined. - * @return {!proto.querier.v1.SelectMergeSpanProfileRequest} returns this - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.clearMaxNodes = function() { - return jspb.Message.setField(this, 6, undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.hasMaxNodes = function() { - return jspb.Message.getField(this, 6) != null; -}; - - -/** - * optional ProfileFormat format = 7; - * @return {!proto.querier.v1.ProfileFormat} - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.getFormat = function() { - return /** @type {!proto.querier.v1.ProfileFormat} */ (jspb.Message.getFieldWithDefault(this, 7, 0)); -}; - - -/** - * @param {!proto.querier.v1.ProfileFormat} value - * @return {!proto.querier.v1.SelectMergeSpanProfileRequest} returns this - */ -proto.querier.v1.SelectMergeSpanProfileRequest.prototype.setFormat = function(value) { - return jspb.Message.setProto3EnumField(this, 7, value); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.SelectMergeSpanProfileResponse.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.SelectMergeSpanProfileResponse.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.SelectMergeSpanProfileResponse} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SelectMergeSpanProfileResponse.toObject = function(includeInstance, msg) { - var f, obj = { - flamegraph: (f = msg.getFlamegraph()) && proto.querier.v1.FlameGraph.toObject(includeInstance, f), - tree: msg.getTree_asB64() - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.SelectMergeSpanProfileResponse} - */ -proto.querier.v1.SelectMergeSpanProfileResponse.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.SelectMergeSpanProfileResponse; - return proto.querier.v1.SelectMergeSpanProfileResponse.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.SelectMergeSpanProfileResponse} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.SelectMergeSpanProfileResponse} - */ -proto.querier.v1.SelectMergeSpanProfileResponse.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = new proto.querier.v1.FlameGraph; - reader.readMessage(value,proto.querier.v1.FlameGraph.deserializeBinaryFromReader); - msg.setFlamegraph(value); - break; - case 2: - var value = /** @type {!Uint8Array} */ (reader.readBytes()); - msg.setTree(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.SelectMergeSpanProfileResponse.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.SelectMergeSpanProfileResponse.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.SelectMergeSpanProfileResponse} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SelectMergeSpanProfileResponse.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getFlamegraph(); - if (f != null) { - writer.writeMessage( - 1, - f, - proto.querier.v1.FlameGraph.serializeBinaryToWriter - ); - } - f = message.getTree_asU8(); - if (f.length > 0) { - writer.writeBytes( - 2, - f - ); - } -}; - - -/** - * optional FlameGraph flamegraph = 1; - * @return {?proto.querier.v1.FlameGraph} - */ -proto.querier.v1.SelectMergeSpanProfileResponse.prototype.getFlamegraph = function() { - return /** @type{?proto.querier.v1.FlameGraph} */ ( - jspb.Message.getWrapperField(this, proto.querier.v1.FlameGraph, 1)); -}; - - -/** - * @param {?proto.querier.v1.FlameGraph|undefined} value - * @return {!proto.querier.v1.SelectMergeSpanProfileResponse} returns this -*/ -proto.querier.v1.SelectMergeSpanProfileResponse.prototype.setFlamegraph = function(value) { - return jspb.Message.setWrapperField(this, 1, value); -}; - - -/** - * Clears the message field making it undefined. - * @return {!proto.querier.v1.SelectMergeSpanProfileResponse} returns this - */ -proto.querier.v1.SelectMergeSpanProfileResponse.prototype.clearFlamegraph = function() { - return this.setFlamegraph(undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.querier.v1.SelectMergeSpanProfileResponse.prototype.hasFlamegraph = function() { - return jspb.Message.getField(this, 1) != null; -}; - - -/** - * optional bytes tree = 2; - * @return {!(string|Uint8Array)} - */ -proto.querier.v1.SelectMergeSpanProfileResponse.prototype.getTree = function() { - return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 2, "")); -}; - - -/** - * optional bytes tree = 2; - * This is a type-conversion wrapper around `getTree()` - * @return {string} - */ -proto.querier.v1.SelectMergeSpanProfileResponse.prototype.getTree_asB64 = function() { - return /** @type {string} */ (jspb.Message.bytesAsB64( - this.getTree())); -}; - - -/** - * optional bytes tree = 2; - * Note that Uint8Array is not supported on all browsers. - * @see http://caniuse.com/Uint8Array - * This is a type-conversion wrapper around `getTree()` - * @return {!Uint8Array} - */ -proto.querier.v1.SelectMergeSpanProfileResponse.prototype.getTree_asU8 = function() { - return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( - this.getTree())); -}; - - -/** - * @param {!(string|Uint8Array)} value - * @return {!proto.querier.v1.SelectMergeSpanProfileResponse} returns this - */ -proto.querier.v1.SelectMergeSpanProfileResponse.prototype.setTree = function(value) { - return jspb.Message.setProto3BytesField(this, 2, value); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.DiffRequest.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.DiffRequest.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.DiffRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.DiffRequest.toObject = function(includeInstance, msg) { - var f, obj = { - left: (f = msg.getLeft()) && proto.querier.v1.SelectMergeStacktracesRequest.toObject(includeInstance, f), - right: (f = msg.getRight()) && proto.querier.v1.SelectMergeStacktracesRequest.toObject(includeInstance, f) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.DiffRequest} - */ -proto.querier.v1.DiffRequest.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.DiffRequest; - return proto.querier.v1.DiffRequest.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.DiffRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.DiffRequest} - */ -proto.querier.v1.DiffRequest.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = new proto.querier.v1.SelectMergeStacktracesRequest; - reader.readMessage(value,proto.querier.v1.SelectMergeStacktracesRequest.deserializeBinaryFromReader); - msg.setLeft(value); - break; - case 2: - var value = new proto.querier.v1.SelectMergeStacktracesRequest; - reader.readMessage(value,proto.querier.v1.SelectMergeStacktracesRequest.deserializeBinaryFromReader); - msg.setRight(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.DiffRequest.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.DiffRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.DiffRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.DiffRequest.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getLeft(); - if (f != null) { - writer.writeMessage( - 1, - f, - proto.querier.v1.SelectMergeStacktracesRequest.serializeBinaryToWriter - ); - } - f = message.getRight(); - if (f != null) { - writer.writeMessage( - 2, - f, - proto.querier.v1.SelectMergeStacktracesRequest.serializeBinaryToWriter - ); - } -}; - - -/** - * optional SelectMergeStacktracesRequest left = 1; - * @return {?proto.querier.v1.SelectMergeStacktracesRequest} - */ -proto.querier.v1.DiffRequest.prototype.getLeft = function() { - return /** @type{?proto.querier.v1.SelectMergeStacktracesRequest} */ ( - jspb.Message.getWrapperField(this, proto.querier.v1.SelectMergeStacktracesRequest, 1)); -}; - - -/** - * @param {?proto.querier.v1.SelectMergeStacktracesRequest|undefined} value - * @return {!proto.querier.v1.DiffRequest} returns this -*/ -proto.querier.v1.DiffRequest.prototype.setLeft = function(value) { - return jspb.Message.setWrapperField(this, 1, value); -}; - - -/** - * Clears the message field making it undefined. - * @return {!proto.querier.v1.DiffRequest} returns this - */ -proto.querier.v1.DiffRequest.prototype.clearLeft = function() { - return this.setLeft(undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.querier.v1.DiffRequest.prototype.hasLeft = function() { - return jspb.Message.getField(this, 1) != null; -}; - - -/** - * optional SelectMergeStacktracesRequest right = 2; - * @return {?proto.querier.v1.SelectMergeStacktracesRequest} - */ -proto.querier.v1.DiffRequest.prototype.getRight = function() { - return /** @type{?proto.querier.v1.SelectMergeStacktracesRequest} */ ( - jspb.Message.getWrapperField(this, proto.querier.v1.SelectMergeStacktracesRequest, 2)); -}; - - -/** - * @param {?proto.querier.v1.SelectMergeStacktracesRequest|undefined} value - * @return {!proto.querier.v1.DiffRequest} returns this -*/ -proto.querier.v1.DiffRequest.prototype.setRight = function(value) { - return jspb.Message.setWrapperField(this, 2, value); -}; - - -/** - * Clears the message field making it undefined. - * @return {!proto.querier.v1.DiffRequest} returns this - */ -proto.querier.v1.DiffRequest.prototype.clearRight = function() { - return this.setRight(undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.querier.v1.DiffRequest.prototype.hasRight = function() { - return jspb.Message.getField(this, 2) != null; -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.DiffResponse.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.DiffResponse.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.DiffResponse} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.DiffResponse.toObject = function(includeInstance, msg) { - var f, obj = { - flamegraph: (f = msg.getFlamegraph()) && proto.querier.v1.FlameGraphDiff.toObject(includeInstance, f) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.DiffResponse} - */ -proto.querier.v1.DiffResponse.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.DiffResponse; - return proto.querier.v1.DiffResponse.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.DiffResponse} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.DiffResponse} - */ -proto.querier.v1.DiffResponse.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = new proto.querier.v1.FlameGraphDiff; - reader.readMessage(value,proto.querier.v1.FlameGraphDiff.deserializeBinaryFromReader); - msg.setFlamegraph(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.DiffResponse.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.DiffResponse.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.DiffResponse} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.DiffResponse.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getFlamegraph(); - if (f != null) { - writer.writeMessage( - 1, - f, - proto.querier.v1.FlameGraphDiff.serializeBinaryToWriter - ); - } -}; - - -/** - * optional FlameGraphDiff flamegraph = 1; - * @return {?proto.querier.v1.FlameGraphDiff} - */ -proto.querier.v1.DiffResponse.prototype.getFlamegraph = function() { - return /** @type{?proto.querier.v1.FlameGraphDiff} */ ( - jspb.Message.getWrapperField(this, proto.querier.v1.FlameGraphDiff, 1)); -}; - - -/** - * @param {?proto.querier.v1.FlameGraphDiff|undefined} value - * @return {!proto.querier.v1.DiffResponse} returns this -*/ -proto.querier.v1.DiffResponse.prototype.setFlamegraph = function(value) { - return jspb.Message.setWrapperField(this, 1, value); -}; - - -/** - * Clears the message field making it undefined. - * @return {!proto.querier.v1.DiffResponse} returns this - */ -proto.querier.v1.DiffResponse.prototype.clearFlamegraph = function() { - return this.setFlamegraph(undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.querier.v1.DiffResponse.prototype.hasFlamegraph = function() { - return jspb.Message.getField(this, 1) != null; -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.querier.v1.FlameGraph.repeatedFields_ = [1,2]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.FlameGraph.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.FlameGraph.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.FlameGraph} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.FlameGraph.toObject = function(includeInstance, msg) { - var f, obj = { - namesList: (f = jspb.Message.getRepeatedField(msg, 1)) == null ? undefined : f, - levelsList: jspb.Message.toObjectList(msg.getLevelsList(), - proto.querier.v1.Level.toObject, includeInstance), - total: jspb.Message.getFieldWithDefault(msg, 3, 0), - maxSelf: jspb.Message.getFieldWithDefault(msg, 4, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.FlameGraph} - */ -proto.querier.v1.FlameGraph.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.FlameGraph; - return proto.querier.v1.FlameGraph.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.FlameGraph} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.FlameGraph} - */ -proto.querier.v1.FlameGraph.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.addNames(value); - break; - case 2: - var value = new proto.querier.v1.Level; - reader.readMessage(value,proto.querier.v1.Level.deserializeBinaryFromReader); - msg.addLevels(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt64()); - msg.setTotal(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt64()); - msg.setMaxSelf(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.FlameGraph.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.FlameGraph.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.FlameGraph} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.FlameGraph.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getNamesList(); - if (f.length > 0) { - writer.writeRepeatedString( - 1, - f - ); - } - f = message.getLevelsList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 2, - f, - proto.querier.v1.Level.serializeBinaryToWriter - ); - } - f = message.getTotal(); - if (f !== 0) { - writer.writeInt64( - 3, - f - ); - } - f = message.getMaxSelf(); - if (f !== 0) { - writer.writeInt64( - 4, - f - ); - } -}; - - -/** - * repeated string names = 1; - * @return {!Array<string>} - */ -proto.querier.v1.FlameGraph.prototype.getNamesList = function() { - return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 1)); -}; - - -/** - * @param {!Array<string>} value - * @return {!proto.querier.v1.FlameGraph} returns this - */ -proto.querier.v1.FlameGraph.prototype.setNamesList = function(value) { - return jspb.Message.setField(this, 1, value || []); -}; - - -/** - * @param {string} value - * @param {number=} opt_index - * @return {!proto.querier.v1.FlameGraph} returns this - */ -proto.querier.v1.FlameGraph.prototype.addNames = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 1, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.querier.v1.FlameGraph} returns this - */ -proto.querier.v1.FlameGraph.prototype.clearNamesList = function() { - return this.setNamesList([]); -}; - - -/** - * repeated Level levels = 2; - * @return {!Array<!proto.querier.v1.Level>} - */ -proto.querier.v1.FlameGraph.prototype.getLevelsList = function() { - return /** @type{!Array<!proto.querier.v1.Level>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.querier.v1.Level, 2)); -}; - - -/** - * @param {!Array<!proto.querier.v1.Level>} value - * @return {!proto.querier.v1.FlameGraph} returns this -*/ -proto.querier.v1.FlameGraph.prototype.setLevelsList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 2, value); -}; - - -/** - * @param {!proto.querier.v1.Level=} opt_value - * @param {number=} opt_index - * @return {!proto.querier.v1.Level} - */ -proto.querier.v1.FlameGraph.prototype.addLevels = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 2, opt_value, proto.querier.v1.Level, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.querier.v1.FlameGraph} returns this - */ -proto.querier.v1.FlameGraph.prototype.clearLevelsList = function() { - return this.setLevelsList([]); -}; - - -/** - * optional int64 total = 3; - * @return {number} - */ -proto.querier.v1.FlameGraph.prototype.getTotal = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.FlameGraph} returns this - */ -proto.querier.v1.FlameGraph.prototype.setTotal = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional int64 max_self = 4; - * @return {number} - */ -proto.querier.v1.FlameGraph.prototype.getMaxSelf = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.FlameGraph} returns this - */ -proto.querier.v1.FlameGraph.prototype.setMaxSelf = function(value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.querier.v1.FlameGraphDiff.repeatedFields_ = [1,2]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.FlameGraphDiff.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.FlameGraphDiff.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.FlameGraphDiff} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.FlameGraphDiff.toObject = function(includeInstance, msg) { - var f, obj = { - namesList: (f = jspb.Message.getRepeatedField(msg, 1)) == null ? undefined : f, - levelsList: jspb.Message.toObjectList(msg.getLevelsList(), - proto.querier.v1.Level.toObject, includeInstance), - total: jspb.Message.getFieldWithDefault(msg, 3, 0), - maxSelf: jspb.Message.getFieldWithDefault(msg, 4, 0), - leftticks: jspb.Message.getFieldWithDefault(msg, 5, 0), - rightticks: jspb.Message.getFieldWithDefault(msg, 6, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.FlameGraphDiff} - */ -proto.querier.v1.FlameGraphDiff.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.FlameGraphDiff; - return proto.querier.v1.FlameGraphDiff.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.FlameGraphDiff} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.FlameGraphDiff} - */ -proto.querier.v1.FlameGraphDiff.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.addNames(value); - break; - case 2: - var value = new proto.querier.v1.Level; - reader.readMessage(value,proto.querier.v1.Level.deserializeBinaryFromReader); - msg.addLevels(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt64()); - msg.setTotal(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt64()); - msg.setMaxSelf(value); - break; - case 5: - var value = /** @type {number} */ (reader.readInt64()); - msg.setLeftticks(value); - break; - case 6: - var value = /** @type {number} */ (reader.readInt64()); - msg.setRightticks(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.FlameGraphDiff.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.FlameGraphDiff.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.FlameGraphDiff} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.FlameGraphDiff.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getNamesList(); - if (f.length > 0) { - writer.writeRepeatedString( - 1, - f - ); - } - f = message.getLevelsList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 2, - f, - proto.querier.v1.Level.serializeBinaryToWriter - ); - } - f = message.getTotal(); - if (f !== 0) { - writer.writeInt64( - 3, - f - ); - } - f = message.getMaxSelf(); - if (f !== 0) { - writer.writeInt64( - 4, - f - ); - } - f = message.getLeftticks(); - if (f !== 0) { - writer.writeInt64( - 5, - f - ); - } - f = message.getRightticks(); - if (f !== 0) { - writer.writeInt64( - 6, - f - ); - } -}; - - -/** - * repeated string names = 1; - * @return {!Array<string>} - */ -proto.querier.v1.FlameGraphDiff.prototype.getNamesList = function() { - return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 1)); -}; - - -/** - * @param {!Array<string>} value - * @return {!proto.querier.v1.FlameGraphDiff} returns this - */ -proto.querier.v1.FlameGraphDiff.prototype.setNamesList = function(value) { - return jspb.Message.setField(this, 1, value || []); -}; - - -/** - * @param {string} value - * @param {number=} opt_index - * @return {!proto.querier.v1.FlameGraphDiff} returns this - */ -proto.querier.v1.FlameGraphDiff.prototype.addNames = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 1, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.querier.v1.FlameGraphDiff} returns this - */ -proto.querier.v1.FlameGraphDiff.prototype.clearNamesList = function() { - return this.setNamesList([]); -}; - - -/** - * repeated Level levels = 2; - * @return {!Array<!proto.querier.v1.Level>} - */ -proto.querier.v1.FlameGraphDiff.prototype.getLevelsList = function() { - return /** @type{!Array<!proto.querier.v1.Level>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.querier.v1.Level, 2)); -}; - - -/** - * @param {!Array<!proto.querier.v1.Level>} value - * @return {!proto.querier.v1.FlameGraphDiff} returns this -*/ -proto.querier.v1.FlameGraphDiff.prototype.setLevelsList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 2, value); -}; - - -/** - * @param {!proto.querier.v1.Level=} opt_value - * @param {number=} opt_index - * @return {!proto.querier.v1.Level} - */ -proto.querier.v1.FlameGraphDiff.prototype.addLevels = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 2, opt_value, proto.querier.v1.Level, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.querier.v1.FlameGraphDiff} returns this - */ -proto.querier.v1.FlameGraphDiff.prototype.clearLevelsList = function() { - return this.setLevelsList([]); -}; - - -/** - * optional int64 total = 3; - * @return {number} - */ -proto.querier.v1.FlameGraphDiff.prototype.getTotal = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.FlameGraphDiff} returns this - */ -proto.querier.v1.FlameGraphDiff.prototype.setTotal = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional int64 max_self = 4; - * @return {number} - */ -proto.querier.v1.FlameGraphDiff.prototype.getMaxSelf = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.FlameGraphDiff} returns this - */ -proto.querier.v1.FlameGraphDiff.prototype.setMaxSelf = function(value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - - -/** - * optional int64 leftTicks = 5; - * @return {number} - */ -proto.querier.v1.FlameGraphDiff.prototype.getLeftticks = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.FlameGraphDiff} returns this - */ -proto.querier.v1.FlameGraphDiff.prototype.setLeftticks = function(value) { - return jspb.Message.setProto3IntField(this, 5, value); -}; - - -/** - * optional int64 rightTicks = 6; - * @return {number} - */ -proto.querier.v1.FlameGraphDiff.prototype.getRightticks = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.FlameGraphDiff} returns this - */ -proto.querier.v1.FlameGraphDiff.prototype.setRightticks = function(value) { - return jspb.Message.setProto3IntField(this, 6, value); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.querier.v1.Level.repeatedFields_ = [1]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.Level.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.Level.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.Level} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.Level.toObject = function(includeInstance, msg) { - var f, obj = { - valuesList: (f = jspb.Message.getRepeatedField(msg, 1)) == null ? undefined : f - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.Level} - */ -proto.querier.v1.Level.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.Level; - return proto.querier.v1.Level.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.Level} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.Level} - */ -proto.querier.v1.Level.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var values = /** @type {!Array<number>} */ (reader.isDelimited() ? reader.readPackedInt64() : [reader.readInt64()]); - for (var i = 0; i < values.length; i++) { - msg.addValues(values[i]); - } - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.Level.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.Level.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.Level} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.Level.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getValuesList(); - if (f.length > 0) { - writer.writePackedInt64( - 1, - f - ); - } -}; - - -/** - * repeated int64 values = 1; - * @return {!Array<number>} - */ -proto.querier.v1.Level.prototype.getValuesList = function() { - return /** @type {!Array<number>} */ (jspb.Message.getRepeatedField(this, 1)); -}; - - -/** - * @param {!Array<number>} value - * @return {!proto.querier.v1.Level} returns this - */ -proto.querier.v1.Level.prototype.setValuesList = function(value) { - return jspb.Message.setField(this, 1, value || []); -}; - - -/** - * @param {number} value - * @param {number=} opt_index - * @return {!proto.querier.v1.Level} returns this - */ -proto.querier.v1.Level.prototype.addValues = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 1, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.querier.v1.Level} returns this - */ -proto.querier.v1.Level.prototype.clearValuesList = function() { - return this.setValuesList([]); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.SelectMergeProfileRequest.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.SelectMergeProfileRequest.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.SelectMergeProfileRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SelectMergeProfileRequest.toObject = function(includeInstance, msg) { - var f, obj = { - profileTypeid: jspb.Message.getFieldWithDefault(msg, 1, ""), - labelSelector: jspb.Message.getFieldWithDefault(msg, 2, ""), - start: jspb.Message.getFieldWithDefault(msg, 3, 0), - end: jspb.Message.getFieldWithDefault(msg, 4, 0), - maxNodes: jspb.Message.getFieldWithDefault(msg, 5, 0), - stackTraceSelector: (f = msg.getStackTraceSelector()) && types_v1_types_pb.StackTraceSelector.toObject(includeInstance, f) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.SelectMergeProfileRequest} - */ -proto.querier.v1.SelectMergeProfileRequest.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.SelectMergeProfileRequest; - return proto.querier.v1.SelectMergeProfileRequest.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.SelectMergeProfileRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.SelectMergeProfileRequest} - */ -proto.querier.v1.SelectMergeProfileRequest.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setProfileTypeid(value); - break; - case 2: - var value = /** @type {string} */ (reader.readString()); - msg.setLabelSelector(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt64()); - msg.setStart(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt64()); - msg.setEnd(value); - break; - case 5: - var value = /** @type {number} */ (reader.readInt64()); - msg.setMaxNodes(value); - break; - case 6: - var value = new types_v1_types_pb.StackTraceSelector; - reader.readMessage(value,types_v1_types_pb.StackTraceSelector.deserializeBinaryFromReader); - msg.setStackTraceSelector(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.SelectMergeProfileRequest.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.SelectMergeProfileRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.SelectMergeProfileRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SelectMergeProfileRequest.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getProfileTypeid(); - if (f.length > 0) { - writer.writeString( - 1, - f - ); - } - f = message.getLabelSelector(); - if (f.length > 0) { - writer.writeString( - 2, - f - ); - } - f = message.getStart(); - if (f !== 0) { - writer.writeInt64( - 3, - f - ); - } - f = message.getEnd(); - if (f !== 0) { - writer.writeInt64( - 4, - f - ); - } - f = /** @type {number} */ (jspb.Message.getField(message, 5)); - if (f != null) { - writer.writeInt64( - 5, - f - ); - } - f = message.getStackTraceSelector(); - if (f != null) { - writer.writeMessage( - 6, - f, - types_v1_types_pb.StackTraceSelector.serializeBinaryToWriter - ); - } -}; - - -/** - * optional string profile_typeID = 1; - * @return {string} - */ -proto.querier.v1.SelectMergeProfileRequest.prototype.getProfileTypeid = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - - -/** - * @param {string} value - * @return {!proto.querier.v1.SelectMergeProfileRequest} returns this - */ -proto.querier.v1.SelectMergeProfileRequest.prototype.setProfileTypeid = function(value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - - -/** - * optional string label_selector = 2; - * @return {string} - */ -proto.querier.v1.SelectMergeProfileRequest.prototype.getLabelSelector = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); -}; - - -/** - * @param {string} value - * @return {!proto.querier.v1.SelectMergeProfileRequest} returns this - */ -proto.querier.v1.SelectMergeProfileRequest.prototype.setLabelSelector = function(value) { - return jspb.Message.setProto3StringField(this, 2, value); -}; - - -/** - * optional int64 start = 3; - * @return {number} - */ -proto.querier.v1.SelectMergeProfileRequest.prototype.getStart = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.SelectMergeProfileRequest} returns this - */ -proto.querier.v1.SelectMergeProfileRequest.prototype.setStart = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional int64 end = 4; - * @return {number} - */ -proto.querier.v1.SelectMergeProfileRequest.prototype.getEnd = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.SelectMergeProfileRequest} returns this - */ -proto.querier.v1.SelectMergeProfileRequest.prototype.setEnd = function(value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - - -/** - * optional int64 max_nodes = 5; - * @return {number} - */ -proto.querier.v1.SelectMergeProfileRequest.prototype.getMaxNodes = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.SelectMergeProfileRequest} returns this - */ -proto.querier.v1.SelectMergeProfileRequest.prototype.setMaxNodes = function(value) { - return jspb.Message.setField(this, 5, value); -}; - - -/** - * Clears the field making it undefined. - * @return {!proto.querier.v1.SelectMergeProfileRequest} returns this - */ -proto.querier.v1.SelectMergeProfileRequest.prototype.clearMaxNodes = function() { - return jspb.Message.setField(this, 5, undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.querier.v1.SelectMergeProfileRequest.prototype.hasMaxNodes = function() { - return jspb.Message.getField(this, 5) != null; -}; - - -/** - * optional types.v1.StackTraceSelector stack_trace_selector = 6; - * @return {?proto.types.v1.StackTraceSelector} - */ -proto.querier.v1.SelectMergeProfileRequest.prototype.getStackTraceSelector = function() { - return /** @type{?proto.types.v1.StackTraceSelector} */ ( - jspb.Message.getWrapperField(this, types_v1_types_pb.StackTraceSelector, 6)); -}; - - -/** - * @param {?proto.types.v1.StackTraceSelector|undefined} value - * @return {!proto.querier.v1.SelectMergeProfileRequest} returns this -*/ -proto.querier.v1.SelectMergeProfileRequest.prototype.setStackTraceSelector = function(value) { - return jspb.Message.setWrapperField(this, 6, value); -}; - - -/** - * Clears the message field making it undefined. - * @return {!proto.querier.v1.SelectMergeProfileRequest} returns this - */ -proto.querier.v1.SelectMergeProfileRequest.prototype.clearStackTraceSelector = function() { - return this.setStackTraceSelector(undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.querier.v1.SelectMergeProfileRequest.prototype.hasStackTraceSelector = function() { - return jspb.Message.getField(this, 6) != null; -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.querier.v1.SelectSeriesRequest.repeatedFields_ = [5]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.SelectSeriesRequest.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.SelectSeriesRequest.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.SelectSeriesRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SelectSeriesRequest.toObject = function(includeInstance, msg) { - var f, obj = { - profileTypeid: jspb.Message.getFieldWithDefault(msg, 1, ""), - labelSelector: jspb.Message.getFieldWithDefault(msg, 2, ""), - start: jspb.Message.getFieldWithDefault(msg, 3, 0), - end: jspb.Message.getFieldWithDefault(msg, 4, 0), - groupByList: (f = jspb.Message.getRepeatedField(msg, 5)) == null ? undefined : f, - step: jspb.Message.getFloatingPointFieldWithDefault(msg, 6, 0.0), - aggregation: jspb.Message.getFieldWithDefault(msg, 7, 0), - stackTraceSelector: (f = msg.getStackTraceSelector()) && types_v1_types_pb.StackTraceSelector.toObject(includeInstance, f) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.SelectSeriesRequest} - */ -proto.querier.v1.SelectSeriesRequest.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.SelectSeriesRequest; - return proto.querier.v1.SelectSeriesRequest.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.SelectSeriesRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.SelectSeriesRequest} - */ -proto.querier.v1.SelectSeriesRequest.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setProfileTypeid(value); - break; - case 2: - var value = /** @type {string} */ (reader.readString()); - msg.setLabelSelector(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt64()); - msg.setStart(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt64()); - msg.setEnd(value); - break; - case 5: - var value = /** @type {string} */ (reader.readString()); - msg.addGroupBy(value); - break; - case 6: - var value = /** @type {number} */ (reader.readDouble()); - msg.setStep(value); - break; - case 7: - var value = /** @type {!proto.types.v1.TimeSeriesAggregationType} */ (reader.readEnum()); - msg.setAggregation(value); - break; - case 8: - var value = new types_v1_types_pb.StackTraceSelector; - reader.readMessage(value,types_v1_types_pb.StackTraceSelector.deserializeBinaryFromReader); - msg.setStackTraceSelector(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.SelectSeriesRequest.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.SelectSeriesRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.SelectSeriesRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SelectSeriesRequest.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getProfileTypeid(); - if (f.length > 0) { - writer.writeString( - 1, - f - ); - } - f = message.getLabelSelector(); - if (f.length > 0) { - writer.writeString( - 2, - f - ); - } - f = message.getStart(); - if (f !== 0) { - writer.writeInt64( - 3, - f - ); - } - f = message.getEnd(); - if (f !== 0) { - writer.writeInt64( - 4, - f - ); - } - f = message.getGroupByList(); - if (f.length > 0) { - writer.writeRepeatedString( - 5, - f - ); - } - f = message.getStep(); - if (f !== 0.0) { - writer.writeDouble( - 6, - f - ); - } - f = /** @type {!proto.types.v1.TimeSeriesAggregationType} */ (jspb.Message.getField(message, 7)); - if (f != null) { - writer.writeEnum( - 7, - f - ); - } - f = message.getStackTraceSelector(); - if (f != null) { - writer.writeMessage( - 8, - f, - types_v1_types_pb.StackTraceSelector.serializeBinaryToWriter - ); - } -}; - - -/** - * optional string profile_typeID = 1; - * @return {string} - */ -proto.querier.v1.SelectSeriesRequest.prototype.getProfileTypeid = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - - -/** - * @param {string} value - * @return {!proto.querier.v1.SelectSeriesRequest} returns this - */ -proto.querier.v1.SelectSeriesRequest.prototype.setProfileTypeid = function(value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - - -/** - * optional string label_selector = 2; - * @return {string} - */ -proto.querier.v1.SelectSeriesRequest.prototype.getLabelSelector = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); -}; - - -/** - * @param {string} value - * @return {!proto.querier.v1.SelectSeriesRequest} returns this - */ -proto.querier.v1.SelectSeriesRequest.prototype.setLabelSelector = function(value) { - return jspb.Message.setProto3StringField(this, 2, value); -}; - - -/** - * optional int64 start = 3; - * @return {number} - */ -proto.querier.v1.SelectSeriesRequest.prototype.getStart = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.SelectSeriesRequest} returns this - */ -proto.querier.v1.SelectSeriesRequest.prototype.setStart = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional int64 end = 4; - * @return {number} - */ -proto.querier.v1.SelectSeriesRequest.prototype.getEnd = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.SelectSeriesRequest} returns this - */ -proto.querier.v1.SelectSeriesRequest.prototype.setEnd = function(value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - - -/** - * repeated string group_by = 5; - * @return {!Array<string>} - */ -proto.querier.v1.SelectSeriesRequest.prototype.getGroupByList = function() { - return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 5)); -}; - - -/** - * @param {!Array<string>} value - * @return {!proto.querier.v1.SelectSeriesRequest} returns this - */ -proto.querier.v1.SelectSeriesRequest.prototype.setGroupByList = function(value) { - return jspb.Message.setField(this, 5, value || []); -}; - - -/** - * @param {string} value - * @param {number=} opt_index - * @return {!proto.querier.v1.SelectSeriesRequest} returns this - */ -proto.querier.v1.SelectSeriesRequest.prototype.addGroupBy = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 5, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.querier.v1.SelectSeriesRequest} returns this - */ -proto.querier.v1.SelectSeriesRequest.prototype.clearGroupByList = function() { - return this.setGroupByList([]); -}; - - -/** - * optional double step = 6; - * @return {number} - */ -proto.querier.v1.SelectSeriesRequest.prototype.getStep = function() { - return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 6, 0.0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.SelectSeriesRequest} returns this - */ -proto.querier.v1.SelectSeriesRequest.prototype.setStep = function(value) { - return jspb.Message.setProto3FloatField(this, 6, value); -}; - - -/** - * optional types.v1.TimeSeriesAggregationType aggregation = 7; - * @return {!proto.types.v1.TimeSeriesAggregationType} - */ -proto.querier.v1.SelectSeriesRequest.prototype.getAggregation = function() { - return /** @type {!proto.types.v1.TimeSeriesAggregationType} */ (jspb.Message.getFieldWithDefault(this, 7, 0)); -}; - - -/** - * @param {!proto.types.v1.TimeSeriesAggregationType} value - * @return {!proto.querier.v1.SelectSeriesRequest} returns this - */ -proto.querier.v1.SelectSeriesRequest.prototype.setAggregation = function(value) { - return jspb.Message.setField(this, 7, value); -}; - - -/** - * Clears the field making it undefined. - * @return {!proto.querier.v1.SelectSeriesRequest} returns this - */ -proto.querier.v1.SelectSeriesRequest.prototype.clearAggregation = function() { - return jspb.Message.setField(this, 7, undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.querier.v1.SelectSeriesRequest.prototype.hasAggregation = function() { - return jspb.Message.getField(this, 7) != null; -}; - - -/** - * optional types.v1.StackTraceSelector stack_trace_selector = 8; - * @return {?proto.types.v1.StackTraceSelector} - */ -proto.querier.v1.SelectSeriesRequest.prototype.getStackTraceSelector = function() { - return /** @type{?proto.types.v1.StackTraceSelector} */ ( - jspb.Message.getWrapperField(this, types_v1_types_pb.StackTraceSelector, 8)); -}; - - -/** - * @param {?proto.types.v1.StackTraceSelector|undefined} value - * @return {!proto.querier.v1.SelectSeriesRequest} returns this -*/ -proto.querier.v1.SelectSeriesRequest.prototype.setStackTraceSelector = function(value) { - return jspb.Message.setWrapperField(this, 8, value); -}; - - -/** - * Clears the message field making it undefined. - * @return {!proto.querier.v1.SelectSeriesRequest} returns this - */ -proto.querier.v1.SelectSeriesRequest.prototype.clearStackTraceSelector = function() { - return this.setStackTraceSelector(undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.querier.v1.SelectSeriesRequest.prototype.hasStackTraceSelector = function() { - return jspb.Message.getField(this, 8) != null; -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.querier.v1.SelectSeriesResponse.repeatedFields_ = [1]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.SelectSeriesResponse.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.SelectSeriesResponse.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.SelectSeriesResponse} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SelectSeriesResponse.toObject = function(includeInstance, msg) { - var f, obj = { - seriesList: jspb.Message.toObjectList(msg.getSeriesList(), - types_v1_types_pb.Series.toObject, includeInstance) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.SelectSeriesResponse} - */ -proto.querier.v1.SelectSeriesResponse.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.SelectSeriesResponse; - return proto.querier.v1.SelectSeriesResponse.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.SelectSeriesResponse} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.SelectSeriesResponse} - */ -proto.querier.v1.SelectSeriesResponse.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = new types_v1_types_pb.Series; - reader.readMessage(value,types_v1_types_pb.Series.deserializeBinaryFromReader); - msg.addSeries(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.SelectSeriesResponse.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.SelectSeriesResponse.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.SelectSeriesResponse} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.SelectSeriesResponse.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getSeriesList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 1, - f, - types_v1_types_pb.Series.serializeBinaryToWriter - ); - } -}; - - -/** - * repeated types.v1.Series series = 1; - * @return {!Array<!proto.types.v1.Series>} - */ -proto.querier.v1.SelectSeriesResponse.prototype.getSeriesList = function() { - return /** @type{!Array<!proto.types.v1.Series>} */ ( - jspb.Message.getRepeatedWrapperField(this, types_v1_types_pb.Series, 1)); -}; - - -/** - * @param {!Array<!proto.types.v1.Series>} value - * @return {!proto.querier.v1.SelectSeriesResponse} returns this -*/ -proto.querier.v1.SelectSeriesResponse.prototype.setSeriesList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 1, value); -}; - - -/** - * @param {!proto.types.v1.Series=} opt_value - * @param {number=} opt_index - * @return {!proto.types.v1.Series} - */ -proto.querier.v1.SelectSeriesResponse.prototype.addSeries = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.types.v1.Series, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.querier.v1.SelectSeriesResponse} returns this - */ -proto.querier.v1.SelectSeriesResponse.prototype.clearSeriesList = function() { - return this.setSeriesList([]); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.AnalyzeQueryRequest.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.AnalyzeQueryRequest.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.AnalyzeQueryRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.AnalyzeQueryRequest.toObject = function(includeInstance, msg) { - var f, obj = { - start: jspb.Message.getFieldWithDefault(msg, 2, 0), - end: jspb.Message.getFieldWithDefault(msg, 3, 0), - query: jspb.Message.getFieldWithDefault(msg, 4, "") - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.AnalyzeQueryRequest} - */ -proto.querier.v1.AnalyzeQueryRequest.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.AnalyzeQueryRequest; - return proto.querier.v1.AnalyzeQueryRequest.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.AnalyzeQueryRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.AnalyzeQueryRequest} - */ -proto.querier.v1.AnalyzeQueryRequest.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 2: - var value = /** @type {number} */ (reader.readInt64()); - msg.setStart(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt64()); - msg.setEnd(value); - break; - case 4: - var value = /** @type {string} */ (reader.readString()); - msg.setQuery(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.AnalyzeQueryRequest.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.AnalyzeQueryRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.AnalyzeQueryRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.AnalyzeQueryRequest.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getStart(); - if (f !== 0) { - writer.writeInt64( - 2, - f - ); - } - f = message.getEnd(); - if (f !== 0) { - writer.writeInt64( - 3, - f - ); - } - f = message.getQuery(); - if (f.length > 0) { - writer.writeString( - 4, - f - ); - } -}; - - -/** - * optional int64 start = 2; - * @return {number} - */ -proto.querier.v1.AnalyzeQueryRequest.prototype.getStart = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.AnalyzeQueryRequest} returns this - */ -proto.querier.v1.AnalyzeQueryRequest.prototype.setStart = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - -/** - * optional int64 end = 3; - * @return {number} - */ -proto.querier.v1.AnalyzeQueryRequest.prototype.getEnd = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.AnalyzeQueryRequest} returns this - */ -proto.querier.v1.AnalyzeQueryRequest.prototype.setEnd = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional string query = 4; - * @return {string} - */ -proto.querier.v1.AnalyzeQueryRequest.prototype.getQuery = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "")); -}; - - -/** - * @param {string} value - * @return {!proto.querier.v1.AnalyzeQueryRequest} returns this - */ -proto.querier.v1.AnalyzeQueryRequest.prototype.setQuery = function(value) { - return jspb.Message.setProto3StringField(this, 4, value); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.querier.v1.AnalyzeQueryResponse.repeatedFields_ = [1]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.AnalyzeQueryResponse.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.AnalyzeQueryResponse.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.AnalyzeQueryResponse} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.AnalyzeQueryResponse.toObject = function(includeInstance, msg) { - var f, obj = { - queryScopesList: jspb.Message.toObjectList(msg.getQueryScopesList(), - proto.querier.v1.QueryScope.toObject, includeInstance), - queryImpact: (f = msg.getQueryImpact()) && proto.querier.v1.QueryImpact.toObject(includeInstance, f) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.AnalyzeQueryResponse} - */ -proto.querier.v1.AnalyzeQueryResponse.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.AnalyzeQueryResponse; - return proto.querier.v1.AnalyzeQueryResponse.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.AnalyzeQueryResponse} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.AnalyzeQueryResponse} - */ -proto.querier.v1.AnalyzeQueryResponse.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = new proto.querier.v1.QueryScope; - reader.readMessage(value,proto.querier.v1.QueryScope.deserializeBinaryFromReader); - msg.addQueryScopes(value); - break; - case 2: - var value = new proto.querier.v1.QueryImpact; - reader.readMessage(value,proto.querier.v1.QueryImpact.deserializeBinaryFromReader); - msg.setQueryImpact(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.AnalyzeQueryResponse.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.AnalyzeQueryResponse.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.AnalyzeQueryResponse} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.AnalyzeQueryResponse.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getQueryScopesList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 1, - f, - proto.querier.v1.QueryScope.serializeBinaryToWriter - ); - } - f = message.getQueryImpact(); - if (f != null) { - writer.writeMessage( - 2, - f, - proto.querier.v1.QueryImpact.serializeBinaryToWriter - ); - } -}; - - -/** - * repeated QueryScope query_scopes = 1; - * @return {!Array<!proto.querier.v1.QueryScope>} - */ -proto.querier.v1.AnalyzeQueryResponse.prototype.getQueryScopesList = function() { - return /** @type{!Array<!proto.querier.v1.QueryScope>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.querier.v1.QueryScope, 1)); -}; - - -/** - * @param {!Array<!proto.querier.v1.QueryScope>} value - * @return {!proto.querier.v1.AnalyzeQueryResponse} returns this -*/ -proto.querier.v1.AnalyzeQueryResponse.prototype.setQueryScopesList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 1, value); -}; - - -/** - * @param {!proto.querier.v1.QueryScope=} opt_value - * @param {number=} opt_index - * @return {!proto.querier.v1.QueryScope} - */ -proto.querier.v1.AnalyzeQueryResponse.prototype.addQueryScopes = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.querier.v1.QueryScope, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.querier.v1.AnalyzeQueryResponse} returns this - */ -proto.querier.v1.AnalyzeQueryResponse.prototype.clearQueryScopesList = function() { - return this.setQueryScopesList([]); -}; - - -/** - * optional QueryImpact query_impact = 2; - * @return {?proto.querier.v1.QueryImpact} - */ -proto.querier.v1.AnalyzeQueryResponse.prototype.getQueryImpact = function() { - return /** @type{?proto.querier.v1.QueryImpact} */ ( - jspb.Message.getWrapperField(this, proto.querier.v1.QueryImpact, 2)); -}; - - -/** - * @param {?proto.querier.v1.QueryImpact|undefined} value - * @return {!proto.querier.v1.AnalyzeQueryResponse} returns this -*/ -proto.querier.v1.AnalyzeQueryResponse.prototype.setQueryImpact = function(value) { - return jspb.Message.setWrapperField(this, 2, value); -}; - - -/** - * Clears the message field making it undefined. - * @return {!proto.querier.v1.AnalyzeQueryResponse} returns this - */ -proto.querier.v1.AnalyzeQueryResponse.prototype.clearQueryImpact = function() { - return this.setQueryImpact(undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.querier.v1.AnalyzeQueryResponse.prototype.hasQueryImpact = function() { - return jspb.Message.getField(this, 2) != null; -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.QueryScope.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.QueryScope.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.QueryScope} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.QueryScope.toObject = function(includeInstance, msg) { - var f, obj = { - componentType: jspb.Message.getFieldWithDefault(msg, 1, ""), - componentCount: jspb.Message.getFieldWithDefault(msg, 2, 0), - blockCount: jspb.Message.getFieldWithDefault(msg, 3, 0), - seriesCount: jspb.Message.getFieldWithDefault(msg, 4, 0), - profileCount: jspb.Message.getFieldWithDefault(msg, 5, 0), - sampleCount: jspb.Message.getFieldWithDefault(msg, 6, 0), - indexBytes: jspb.Message.getFieldWithDefault(msg, 7, 0), - profileBytes: jspb.Message.getFieldWithDefault(msg, 8, 0), - symbolBytes: jspb.Message.getFieldWithDefault(msg, 9, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.QueryScope} - */ -proto.querier.v1.QueryScope.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.QueryScope; - return proto.querier.v1.QueryScope.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.QueryScope} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.QueryScope} - */ -proto.querier.v1.QueryScope.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setComponentType(value); - break; - case 2: - var value = /** @type {number} */ (reader.readUint64()); - msg.setComponentCount(value); - break; - case 3: - var value = /** @type {number} */ (reader.readUint64()); - msg.setBlockCount(value); - break; - case 4: - var value = /** @type {number} */ (reader.readUint64()); - msg.setSeriesCount(value); - break; - case 5: - var value = /** @type {number} */ (reader.readUint64()); - msg.setProfileCount(value); - break; - case 6: - var value = /** @type {number} */ (reader.readUint64()); - msg.setSampleCount(value); - break; - case 7: - var value = /** @type {number} */ (reader.readUint64()); - msg.setIndexBytes(value); - break; - case 8: - var value = /** @type {number} */ (reader.readUint64()); - msg.setProfileBytes(value); - break; - case 9: - var value = /** @type {number} */ (reader.readUint64()); - msg.setSymbolBytes(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.QueryScope.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.QueryScope.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.QueryScope} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.QueryScope.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getComponentType(); - if (f.length > 0) { - writer.writeString( - 1, - f - ); - } - f = message.getComponentCount(); - if (f !== 0) { - writer.writeUint64( - 2, - f - ); - } - f = message.getBlockCount(); - if (f !== 0) { - writer.writeUint64( - 3, - f - ); - } - f = message.getSeriesCount(); - if (f !== 0) { - writer.writeUint64( - 4, - f - ); - } - f = message.getProfileCount(); - if (f !== 0) { - writer.writeUint64( - 5, - f - ); - } - f = message.getSampleCount(); - if (f !== 0) { - writer.writeUint64( - 6, - f - ); - } - f = message.getIndexBytes(); - if (f !== 0) { - writer.writeUint64( - 7, - f - ); - } - f = message.getProfileBytes(); - if (f !== 0) { - writer.writeUint64( - 8, - f - ); - } - f = message.getSymbolBytes(); - if (f !== 0) { - writer.writeUint64( - 9, - f - ); - } -}; - - -/** - * optional string component_type = 1; - * @return {string} - */ -proto.querier.v1.QueryScope.prototype.getComponentType = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - - -/** - * @param {string} value - * @return {!proto.querier.v1.QueryScope} returns this - */ -proto.querier.v1.QueryScope.prototype.setComponentType = function(value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - - -/** - * optional uint64 component_count = 2; - * @return {number} - */ -proto.querier.v1.QueryScope.prototype.getComponentCount = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.QueryScope} returns this - */ -proto.querier.v1.QueryScope.prototype.setComponentCount = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - -/** - * optional uint64 block_count = 3; - * @return {number} - */ -proto.querier.v1.QueryScope.prototype.getBlockCount = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.QueryScope} returns this - */ -proto.querier.v1.QueryScope.prototype.setBlockCount = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional uint64 series_count = 4; - * @return {number} - */ -proto.querier.v1.QueryScope.prototype.getSeriesCount = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.QueryScope} returns this - */ -proto.querier.v1.QueryScope.prototype.setSeriesCount = function(value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - - -/** - * optional uint64 profile_count = 5; - * @return {number} - */ -proto.querier.v1.QueryScope.prototype.getProfileCount = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.QueryScope} returns this - */ -proto.querier.v1.QueryScope.prototype.setProfileCount = function(value) { - return jspb.Message.setProto3IntField(this, 5, value); -}; - - -/** - * optional uint64 sample_count = 6; - * @return {number} - */ -proto.querier.v1.QueryScope.prototype.getSampleCount = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.QueryScope} returns this - */ -proto.querier.v1.QueryScope.prototype.setSampleCount = function(value) { - return jspb.Message.setProto3IntField(this, 6, value); -}; - - -/** - * optional uint64 index_bytes = 7; - * @return {number} - */ -proto.querier.v1.QueryScope.prototype.getIndexBytes = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.QueryScope} returns this - */ -proto.querier.v1.QueryScope.prototype.setIndexBytes = function(value) { - return jspb.Message.setProto3IntField(this, 7, value); -}; - - -/** - * optional uint64 profile_bytes = 8; - * @return {number} - */ -proto.querier.v1.QueryScope.prototype.getProfileBytes = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 8, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.QueryScope} returns this - */ -proto.querier.v1.QueryScope.prototype.setProfileBytes = function(value) { - return jspb.Message.setProto3IntField(this, 8, value); -}; - - -/** - * optional uint64 symbol_bytes = 9; - * @return {number} - */ -proto.querier.v1.QueryScope.prototype.getSymbolBytes = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 9, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.QueryScope} returns this - */ -proto.querier.v1.QueryScope.prototype.setSymbolBytes = function(value) { - return jspb.Message.setProto3IntField(this, 9, value); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.querier.v1.QueryImpact.prototype.toObject = function(opt_includeInstance) { - return proto.querier.v1.QueryImpact.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.querier.v1.QueryImpact} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.QueryImpact.toObject = function(includeInstance, msg) { - var f, obj = { - totalBytesInTimeRange: jspb.Message.getFieldWithDefault(msg, 2, 0), - totalQueriedSeries: jspb.Message.getFieldWithDefault(msg, 3, 0), - deduplicationNeeded: jspb.Message.getBooleanFieldWithDefault(msg, 4, false) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.querier.v1.QueryImpact} - */ -proto.querier.v1.QueryImpact.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.querier.v1.QueryImpact; - return proto.querier.v1.QueryImpact.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.querier.v1.QueryImpact} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.querier.v1.QueryImpact} - */ -proto.querier.v1.QueryImpact.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 2: - var value = /** @type {number} */ (reader.readUint64()); - msg.setTotalBytesInTimeRange(value); - break; - case 3: - var value = /** @type {number} */ (reader.readUint64()); - msg.setTotalQueriedSeries(value); - break; - case 4: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setDeduplicationNeeded(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.querier.v1.QueryImpact.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.querier.v1.QueryImpact.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.querier.v1.QueryImpact} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.querier.v1.QueryImpact.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getTotalBytesInTimeRange(); - if (f !== 0) { - writer.writeUint64( - 2, - f - ); - } - f = message.getTotalQueriedSeries(); - if (f !== 0) { - writer.writeUint64( - 3, - f - ); - } - f = message.getDeduplicationNeeded(); - if (f) { - writer.writeBool( - 4, - f - ); - } -}; - - -/** - * optional uint64 total_bytes_in_time_range = 2; - * @return {number} - */ -proto.querier.v1.QueryImpact.prototype.getTotalBytesInTimeRange = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.QueryImpact} returns this - */ -proto.querier.v1.QueryImpact.prototype.setTotalBytesInTimeRange = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - -/** - * optional uint64 total_queried_series = 3; - * @return {number} - */ -proto.querier.v1.QueryImpact.prototype.getTotalQueriedSeries = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.querier.v1.QueryImpact} returns this - */ -proto.querier.v1.QueryImpact.prototype.setTotalQueriedSeries = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional bool deduplication_needed = 4; - * @return {boolean} - */ -proto.querier.v1.QueryImpact.prototype.getDeduplicationNeeded = function() { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 4, false)); -}; - - -/** - * @param {boolean} value - * @return {!proto.querier.v1.QueryImpact} returns this - */ -proto.querier.v1.QueryImpact.prototype.setDeduplicationNeeded = function(value) { - return jspb.Message.setProto3BooleanField(this, 4, value); -}; - - -/** - * @enum {number} - */ -proto.querier.v1.ProfileFormat = { - PROFILE_FORMAT_UNSPECIFIED: 0, - PROFILE_FORMAT_FLAMEGRAPH: 1, - PROFILE_FORMAT_TREE: 2 -}; - -goog.object.extend(exports, proto.querier.v1); diff --git a/pyroscope/render.js b/pyroscope/render.js deleted file mode 100644 index 1e637552..00000000 --- a/pyroscope/render.js +++ /dev/null @@ -1,241 +0,0 @@ -const { parseQuery } = require('./shared') -const { mergeStackTraces } = require('./merge_stack_traces') -const querierMessages = require('./querier_pb') -const { selectSeriesImpl } = require('./select_series') - -const render = async (req, res) => { - const query = req.query.query - const parsedQuery = parseQuery(query) - const fromTimeSec = req.query.from - ? Math.floor(parseInt(req.query.from) / 1000) - : Math.floor((Date.now() - 1000 * 60 * 60 * 48) / 1000) - const toTimeSec = req.query.until - ? Math.floor(parseInt(req.query.until) / 1000) - : Math.floor((Date.now() - 1000 * 60 * 60 * 48) / 1000) - if (!parsedQuery) { - return res.code(400).send('Invalid query') - } - const groupBy = req.query.groupBy || [] - let agg = '' - switch (req.query.aggregation) { - case 'sum': - agg = 'sum' - break - case 'avg': - agg = 'avg' - break - } - if (req.query.format === 'dot') { - return res.code(400).send('Dot format is not supported') - } - const promises = [] - promises.push(mergeStackTraces( - parsedQuery.typeDesc, - '{' + parsedQuery.labelSelector + '}', - fromTimeSec, - toTimeSec, - req.log)) - - const timelineStep = calcIntervalSec(fromTimeSec, toTimeSec) - promises.push(selectSeriesImpl( - fromTimeSec, - toTimeSec, - { - getProfileTypeid: () => parsedQuery.typeId, - getLabelSelector: () => `{${parsedQuery.labelSelector}}`, - getGroupByList: () => groupBy, - getStep: () => timelineStep, - getAggregation: () => agg - } - )) - const [bMergeStackTrace, selectSeries] = - await Promise.all(promises) - const mergeStackTrace = querierMessages.SelectMergeStacktracesResponse.deserializeBinary(bMergeStackTrace) - let pTimeline = null - for (const series of selectSeries.getSeriesList()) { - if (!pTimeline) { - pTimeline = timeline(series, - fromTimeSec * 1000, - toTimeSec * 1000, - timelineStep) - continue - } - const _timeline = timeline(series, - fromTimeSec * 1000, - toTimeSec * 1000, - timelineStep) - pTimeline.samples = pTimeline.samples.map((v, i) => v + _timeline.samples[i]) - } - const fb = toFlamebearer(mergeStackTrace.getFlamegraph(), parsedQuery.profileType) - fb.flamebearerProfileV1.timeline = pTimeline - - if (groupBy.length > 0) { - const pGroupedTimelines = {} - fb.flamebearerProfileV1.groups = {} - for (const series of selectSeries.getSeriesList()) { - const _key = {} - for (const label of series.getLabelsList()) { - if (groupBy.includes(label.getName())) { - _key[label.getName()] = label.getValue() - } - } - const key = '{' + Object.entries(_key).map(e => `${e[0]}=${JSON.stringify(e[1])}`) - .sort().join(', ') + '}' - if (!pGroupedTimelines[key]) { - pGroupedTimelines[key] = timeline(series, - fromTimeSec * 1000, - toTimeSec * 1000, - timelineStep) - } else { - const _timeline = timeline(series, - fromTimeSec * 1000, - toTimeSec * 1000, - timelineStep) - pGroupedTimelines[key].samples = pGroupedTimelines[key].samples.map((v, i) => v + _timeline.samples[i]) - } - } - fb.flamebearerProfileV1.groups = pGroupedTimelines - } - res.code(200) - res.headers({ 'Content-Type': 'application/json' }) - return res.send(Buffer.from(JSON.stringify(fb.flamebearerProfileV1))) -} - -/** - * - * @param fg - * @param profileType - * @returns {Flamebearer} - */ -function toFlamebearer (fg, profileType) { - if (!fg) { - fg = new querierMessages.FlameGraph() - } - let unit = profileType.getSampleUnit() - let sampleRate = 100 - switch (profileType.getSampleType()) { - case 'inuse_objects': - case 'alloc_objects': - case 'goroutine': - case 'samples': - unit = 'objects' - break - case 'cpu': - unit = 'samples' - sampleRate = 1000000000 - } - /** @type {flamebearerV1} */ - const flameBearer = { - levels: fg.getLevelsList().map(l => l.getValuesList().map(v => v)), - maxSelf: fg.getMaxSelf(), - names: fg.getNamesList(), - numTicks: fg.getTotal() - } - /** @type {flamebearerMetadataV1} */ - const metadata = { - format: 'single', - units: unit, - name: profileType.getSampleType(), - sampleRate: sampleRate - } - - return { - version: 1, - flamebearerProfileV1: { - metadata: metadata, - flamebearer: flameBearer - } - } -} - -/** - * - * @param fromSec {number} - * @param toSec {number} - * @returns {number} - */ -function calcIntervalSec (fromSec, toSec) { - return Math.max(Math.ceil((toSec - fromSec) / 1500), 15) -} - -/** - * - * @param series - * @param startMs - * @param endMs - * @param durationDeltaSec - * @returns {flamebearerTimelineV1} - */ -function timeline (series, startMs, endMs, durationDeltaSec) { - const durationDeltaMs = durationDeltaSec * 1000 - startMs = Math.floor(startMs / durationDeltaMs) * durationDeltaMs - endMs = Math.floor(endMs / durationDeltaMs) * durationDeltaMs - const startS = Math.floor(startMs / 1000) - /** @type {flamebearerTimelineV1} */ - const timeline = { - durationDelta: durationDeltaSec, - startTime: startS, - samples: [] - } - if (startMs >= endMs) { - return timeline - } - const points = boundPointsToWindow(series.getPointsList(), startMs, endMs) - if (points.length < 1) { - const n = sizeToBackfill(startMs, endMs, durationDeltaSec) - if (n > 0) { - timeline.samples = new Array(n).fill(0) - } - return timeline - } - - let n = sizeToBackfill(startMs, parseInt(points[0].getTimestamp()), durationDeltaSec) - const samples = n > 0 ? Array(n).fill(0) : [] - let prev = points[0] - for (const p of points) { - n = sizeToBackfill(parseInt(prev.getTimestamp()), parseInt(p.getTimestamp()), durationDeltaSec) - Array.prototype.push.apply(samples, new Array(Math.max(0, n - 1)).fill(0)) - samples.push(p.getValue()) - prev = p - } - Array.prototype.push.apply(samples, - new Array(Math.max(0, sizeToBackfill(startMs, endMs, durationDeltaSec) - samples.length)) - .fill(0) - ) - timeline.samples = samples - return timeline -} - -/** - * - * @param points {[]} - * @param startMs {number} - * @param endMs {number} - */ -function boundPointsToWindow (points, startMs, endMs) { - const startIdx = points.findIndex((v) => v.getTimestamp() >= startMs) - const endIdx = points.findLastIndex((v) => v.getTimestamp() < endMs) - return points.slice(startIdx, endIdx + 1) -} - -/** - * - * @param startMs {number} - * @param endMs {number} - * @param stepSec {number} - * @returns {number} - */ -function sizeToBackfill (startMs, endMs, stepSec) { - return Math.floor((endMs - startMs) / (stepSec * 1000)) -} - - -const init = (fastify) => { - fastify.get('/pyroscope/render', render) -} - -module.exports = { - init, - parseQuery, - toFlamebearer -} diff --git a/pyroscope/render_diff.js b/pyroscope/render_diff.js deleted file mode 100644 index e8be19cd..00000000 --- a/pyroscope/render_diff.js +++ /dev/null @@ -1,82 +0,0 @@ -const { parseQuery, toFlamebearer } = require('./render') -const { importStackTraces, newCtxIdx } = require('./merge_stack_traces') -const pprofBin = require('./pprof-bin/pkg') -const querierMessages = require('./querier_pb') -const types = require('./types/v1/types_pb') - -const renderDiff = async (req, res) => { - const leftCtxIdx = newCtxIdx() - const rightCtxIdx = newCtxIdx() - try { - const [leftQuery, leftFromTimeSec, leftToTimeSec] = - parseParams(req.query.leftQuery, req.query.leftFrom, req.query.leftUntil); - const [rightQuery, rightFromTimeSec, rightToTimeSec] = - parseParams(req.query.rightQuery, req.query.rightFrom, req.query.rightUntil); - if (leftQuery.typeId !== rightQuery.typeId) { - res.code(400).send('Different type IDs') - return - } - - await importStackTraces(leftQuery.typeDesc, '{' + leftQuery.labelSelector + '}', leftFromTimeSec, leftToTimeSec, req.log, leftCtxIdx, true) - - await importStackTraces(rightQuery.typeDesc, '{' + rightQuery.labelSelector + '}', rightFromTimeSec, rightToTimeSec, req.log, rightCtxIdx, true) - const flamegraphDiffBin = pprofBin.diff_tree(leftCtxIdx, rightCtxIdx, - `${leftQuery.typeDesc.sampleType}:${leftQuery.typeDesc.sampleUnit}`) - const profileType = new types.ProfileType() - profileType.setId(leftQuery.typeId) - profileType.setName(leftQuery.typeDesc.type) - profileType.setSampleType(leftQuery.typeDesc.sampleType) - profileType.setSampleUnit(leftQuery.typeDesc.sampleUnit) - profileType.setPeriodType(leftQuery.typeDesc.periodType) - profileType.setPeriodUnit(leftQuery.typeDesc.periodUnit) - const diff = querierMessages.FlameGraphDiff.deserializeBinary(flamegraphDiffBin) - return res.code(200).send(diffToFlamegraph(diff, profileType).flamebearerProfileV1) - } finally { - pprofBin.drop_tree(leftCtxIdx) - pprofBin.drop_tree(rightCtxIdx) - } -} - -/** - * - * @param diff - * @param type - */ -const diffToFlamegraph = (diff, type) => { - const fg = new querierMessages.FlameGraph() - fg.setNamesList(diff.getNamesList()) - fg.setLevelsList(diff.getLevelsList()) - fg.setTotal(diff.getTotal()) - fg.setMaxSelf(diff.getMaxSelf()) - const fb = toFlamebearer(fg, type) - fb.flamebearerProfileV1.leftTicks = diff.getLeftticks() - fb.flamebearerProfileV1.rightTicks = diff.getRightticks() - fb.flamebearerProfileV1.metadata = { - ...(fb.flamebearerProfileV1.metadata || {}), - format: 'double' - } - return fb -} - -const parseParams = (query, from, until) => { - const parsedQuery = parseQuery(query) - const fromTimeSec = from - ? Math.floor(parseInt(from) / 1000) - : Math.floor((Date.now() - 1000 * 60 * 60 * 48) / 1000) - const toTimeSec = until - ? Math.floor(parseInt(until) / 1000) - : Math.floor((Date.now() - 1000 * 60 * 60 * 48) / 1000) - if (!parsedQuery) { - throw new Error('Invalid query') - } - return [parsedQuery, fromTimeSec, toTimeSec] -} - -const init = (fastify) => { - fastify.get('/pyroscope/render-diff', renderDiff) -} - -module.exports = { - renderDiff, - init -} diff --git a/pyroscope/select_series.js b/pyroscope/select_series.js deleted file mode 100644 index f25db50c..00000000 --- a/pyroscope/select_series.js +++ /dev/null @@ -1,141 +0,0 @@ -const { QrynBadRequest } = require('../lib/handlers/errors') -const { parseTypeId, serviceNameSelectorQuery, labelSelectorQuery } = require('./shared') -const { clusterName } = require('../common') -const Sql = require('@cloki/clickhouse-sql') -const { DATABASE_NAME } = require('../lib/utils') -const types = require('./types/v1/types_pb') -const clickhouse = require('../lib/db/clickhouse') -const messages = require('./querier_pb') - -const selectSeriesImpl = async (fromTimeSec, toTimeSec, payload) => { - const _req = payload - let typeID = _req.getProfileTypeid && _req.getProfileTypeid() - if (!typeID) { - throw new QrynBadRequest('No type provided') - } - typeID = parseTypeId(typeID) - if (!typeID) { - throw new QrynBadRequest('Invalid type provided') - } - const dist = clusterName ? '_dist' : '' - const sampleTypeId = typeID.sampleType + ':' + typeID.sampleUnit - const labelSelector = _req.getLabelSelector && _req.getLabelSelector() - let groupBy = _req.getGroupByList && _req.getGroupByList() - groupBy = groupBy && groupBy.length ? groupBy : null - const step = _req.getStep && parseInt(_req.getStep()) - if (!step || isNaN(step)) { - throw new QrynBadRequest('No step provided') - } - const aggregation = _req.getAggregation && _req.getAggregation() - - const typeIdSelector = Sql.Eq( - 'type_id', - Sql.val(`${typeID.type}:${typeID.periodType}:${typeID.periodUnit}`)) - const serviceNameSelector = serviceNameSelectorQuery(labelSelector) - - const idxReq = (new Sql.Select()) - .select(new Sql.Raw('fingerprint')) - .from(`${DATABASE_NAME()}.profiles_series_gin`) - .where( - Sql.And( - typeIdSelector, - serviceNameSelector, - Sql.Gte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)}))`)), - Sql.Lte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)}))`)), - Sql.Eq(new Sql.Raw( - `has(sample_types_units, (${Sql.quoteVal(typeID.sampleType)}, ${Sql.quoteVal(typeID.sampleUnit)}))`), - 1) - ) - ) - labelSelectorQuery(idxReq, labelSelector) - - const withIdxReq = (new Sql.With('idx', idxReq, !!clusterName)) - - let tagsReq = 'arraySort(p.tags)' - if (groupBy) { - tagsReq = `arraySort(arrayFilter(x -> x.1 in (${groupBy.map(g => Sql.quoteVal(g)).join(',')}), p.tags))` - } - - const labelsReq = (new Sql.Select()).with(withIdxReq).select( - 'fingerprint', - [new Sql.Raw(tagsReq), 'tags'], - [groupBy ? new Sql.Raw('cityHash64(tags)') : 'fingerprint', 'new_fingerprint'] - ).distinct(true).from([`${DATABASE_NAME()}.profiles_series`, 'p']) - .where(Sql.And( - new Sql.In('fingerprint', 'IN', new Sql.WithReference(withIdxReq)), - Sql.Gte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(fromTimeSec)}))`)), - Sql.Lte('date', new Sql.Raw(`toDate(FROM_UNIXTIME(${Math.floor(toTimeSec)}))`)), - typeIdSelector, - serviceNameSelector - )) - - const withLabelsReq = new Sql.With('labels', labelsReq, !!clusterName) - - let valueCol = new Sql.Raw( - `sum(toFloat64(arrayFirst(x -> x.1 == ${Sql.quoteVal(sampleTypeId)}, p.values_agg).2))`) - if (aggregation === types.TimeSeriesAggregationType.TIME_SERIES_AGGREGATION_TYPE_AVERAGE) { - valueCol = new Sql.Raw( - `sum(toFloat64(arrayFirst(x -> x.1 == ${Sql.quoteVal(sampleTypeId)}).2, p.values_agg)) / ` + - `sum(toFloat64(arrayFirst(x -> x.1 == ${Sql.quoteVal(sampleTypeId)}).3, p.values_agg))` - ) - } - - const mainReq = (new Sql.Select()).with(withIdxReq, withLabelsReq).select( - [new Sql.Raw(`intDiv(p.timestamp_ns, 1000000000 * ${step}) * ${step} * 1000`), 'timestamp_ms'], - [new Sql.Raw('labels.new_fingerprint'), 'fingerprint'], - [new Sql.Raw('min(labels.tags)'), 'labels'], - [valueCol, 'value'] - ).from([`${DATABASE_NAME()}.profiles${dist}`, 'p']).join( - [new Sql.WithReference(withLabelsReq), 'labels'], - 'ANY LEFT', - Sql.Eq(new Sql.Raw('p.fingerprint'), new Sql.Raw('labels.fingerprint')) - ).where( - Sql.And( - new Sql.In('p.fingerprint', 'IN', new Sql.WithReference(withIdxReq)), - Sql.Gte('p.timestamp_ns', new Sql.Raw(`${fromTimeSec}000000000`)), - Sql.Lt('p.timestamp_ns', new Sql.Raw(`${toTimeSec}000000000`)), - typeIdSelector, - serviceNameSelector - ) - ).groupBy('timestamp_ms', 'fingerprint') - .orderBy(['fingerprint', 'ASC'], ['timestamp_ms', 'ASC']) - const strMainReq = mainReq.toString() - const chRes = await clickhouse - .rawRequest(strMainReq + ' FORMAT JSON', null, DATABASE_NAME()) - - let lastFingerprint = null - const seriesList = [] - let lastSeries = null - let lastPoints = [] - for (let i = 0; i < chRes.data.data.length; i++) { - const e = chRes.data.data[i] - if (lastFingerprint !== e.fingerprint) { - lastFingerprint = e.fingerprint - lastSeries && lastSeries.setPointsList(lastPoints) - lastSeries && seriesList.push(lastSeries) - lastPoints = [] - lastSeries = new types.Series() - lastSeries.setLabelsList(e.labels.map(l => { - const lp = new types.LabelPair() - lp.setName(l[0]) - lp.setValue(l[1]) - return lp - })) - } - - const p = new types.Point() - p.setValue(e.value) - p.setTimestamp(e.timestamp_ms) - lastPoints.push(p) - } - lastSeries && lastSeries.setPointsList(lastPoints) - lastSeries && seriesList.push(lastSeries) - - const resp = new messages.SelectSeriesResponse() - resp.setSeriesList(seriesList) - return resp -} - -module.exports = { - selectSeriesImpl -} diff --git a/pyroscope/settings.js b/pyroscope/settings.js deleted file mode 100644 index 98b5dc42..00000000 --- a/pyroscope/settings.js +++ /dev/null @@ -1,31 +0,0 @@ -const messages = require('./settings_pb') -const services = require('./settings_grpc_pb') -const { parser, wrapResponse } = require('./shared') -const parsers = require('./json_parsers') - -const get = (req, res) => { - const _res = new messages.GetSettingsResponse() - const s = new messages.Setting() - s.setName('pluginSettings') - s.setValue('{}') - s.setModifiedat(Date.now()) - _res.setSettingsList([s]) - return _res -} - -module.exports.init = (fastify) => { - const fns = { - get: get - } - const jsonParsers = { - get: parsers.settingsGet - } - for (const name of Object.keys(fns)) { - fastify.post(services.SettingsServiceService[name].path, (req, res) => { - return wrapResponse(fns[name])(req, res) - }, { - 'application/json': jsonParsers[name], - '*': parser(services.SettingsServiceService[name].requestType) - }) - } -} diff --git a/pyroscope/settings_grpc_pb.js b/pyroscope/settings_grpc_pb.js deleted file mode 100644 index 0797ea17..00000000 --- a/pyroscope/settings_grpc_pb.js +++ /dev/null @@ -1,77 +0,0 @@ -// GENERATED CODE -- DO NOT EDIT! - -'use strict'; -var grpc = require('@grpc/grpc-js'); -var settings_pb = require('./settings_pb.js'); - -function serialize_settings_v1_GetSettingsRequest(arg) { - if (!(arg instanceof settings_pb.GetSettingsRequest)) { - throw new Error('Expected argument of type settings.v1.GetSettingsRequest'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_settings_v1_GetSettingsRequest(buffer_arg) { - return settings_pb.GetSettingsRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_settings_v1_GetSettingsResponse(arg) { - if (!(arg instanceof settings_pb.GetSettingsResponse)) { - throw new Error('Expected argument of type settings.v1.GetSettingsResponse'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_settings_v1_GetSettingsResponse(buffer_arg) { - return settings_pb.GetSettingsResponse.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_settings_v1_SetSettingsRequest(arg) { - if (!(arg instanceof settings_pb.SetSettingsRequest)) { - throw new Error('Expected argument of type settings.v1.SetSettingsRequest'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_settings_v1_SetSettingsRequest(buffer_arg) { - return settings_pb.SetSettingsRequest.deserializeBinary(new Uint8Array(buffer_arg)); -} - -function serialize_settings_v1_SetSettingsResponse(arg) { - if (!(arg instanceof settings_pb.SetSettingsResponse)) { - throw new Error('Expected argument of type settings.v1.SetSettingsResponse'); - } - return Buffer.from(arg.serializeBinary()); -} - -function deserialize_settings_v1_SetSettingsResponse(buffer_arg) { - return settings_pb.SetSettingsResponse.deserializeBinary(new Uint8Array(buffer_arg)); -} - - -var SettingsServiceService = exports.SettingsServiceService = { - get: { - path: '/settings.v1.SettingsService/Get', - requestStream: false, - responseStream: false, - requestType: settings_pb.GetSettingsRequest, - responseType: settings_pb.GetSettingsResponse, - requestSerialize: serialize_settings_v1_GetSettingsRequest, - requestDeserialize: deserialize_settings_v1_GetSettingsRequest, - responseSerialize: serialize_settings_v1_GetSettingsResponse, - responseDeserialize: deserialize_settings_v1_GetSettingsResponse, - }, - set: { - path: '/settings.v1.SettingsService/Set', - requestStream: false, - responseStream: false, - requestType: settings_pb.SetSettingsRequest, - responseType: settings_pb.SetSettingsResponse, - requestSerialize: serialize_settings_v1_SetSettingsRequest, - requestDeserialize: deserialize_settings_v1_SetSettingsRequest, - responseSerialize: serialize_settings_v1_SetSettingsResponse, - responseDeserialize: deserialize_settings_v1_SetSettingsResponse, - }, -}; - -exports.SettingsServiceClient = grpc.makeGenericClientConstructor(SettingsServiceService); diff --git a/pyroscope/settings_pb.js b/pyroscope/settings_pb.js deleted file mode 100644 index c8a073e7..00000000 --- a/pyroscope/settings_pb.js +++ /dev/null @@ -1,887 +0,0 @@ -// source: settings.proto -/** - * @fileoverview - * @enhanceable - * @suppress {missingRequire} reports error on implicit type usages. - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! -/* eslint-disable */ -// @ts-nocheck - -var jspb = require('google-protobuf'); -var goog = jspb; -var global = (function() { - if (this) { return this; } - if (typeof window !== 'undefined') { return window; } - if (typeof global !== 'undefined') { return global; } - if (typeof self !== 'undefined') { return self; } - return Function('return this')(); -}.call(null)); - -goog.exportSymbol('proto.settings.v1.GetSettingsRequest', null, global); -goog.exportSymbol('proto.settings.v1.GetSettingsResponse', null, global); -goog.exportSymbol('proto.settings.v1.SetSettingsRequest', null, global); -goog.exportSymbol('proto.settings.v1.SetSettingsResponse', null, global); -goog.exportSymbol('proto.settings.v1.Setting', null, global); -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.settings.v1.GetSettingsRequest = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.settings.v1.GetSettingsRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.settings.v1.GetSettingsRequest.displayName = 'proto.settings.v1.GetSettingsRequest'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.settings.v1.GetSettingsResponse = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.settings.v1.GetSettingsResponse.repeatedFields_, null); -}; -goog.inherits(proto.settings.v1.GetSettingsResponse, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.settings.v1.GetSettingsResponse.displayName = 'proto.settings.v1.GetSettingsResponse'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.settings.v1.SetSettingsRequest = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.settings.v1.SetSettingsRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.settings.v1.SetSettingsRequest.displayName = 'proto.settings.v1.SetSettingsRequest'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.settings.v1.SetSettingsResponse = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.settings.v1.SetSettingsResponse, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.settings.v1.SetSettingsResponse.displayName = 'proto.settings.v1.SetSettingsResponse'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.settings.v1.Setting = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.settings.v1.Setting, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.settings.v1.Setting.displayName = 'proto.settings.v1.Setting'; -} - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.settings.v1.GetSettingsRequest.prototype.toObject = function(opt_includeInstance) { - return proto.settings.v1.GetSettingsRequest.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.settings.v1.GetSettingsRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.settings.v1.GetSettingsRequest.toObject = function(includeInstance, msg) { - var f, obj = { - - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.settings.v1.GetSettingsRequest} - */ -proto.settings.v1.GetSettingsRequest.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.settings.v1.GetSettingsRequest; - return proto.settings.v1.GetSettingsRequest.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.settings.v1.GetSettingsRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.settings.v1.GetSettingsRequest} - */ -proto.settings.v1.GetSettingsRequest.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.settings.v1.GetSettingsRequest.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.settings.v1.GetSettingsRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.settings.v1.GetSettingsRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.settings.v1.GetSettingsRequest.serializeBinaryToWriter = function(message, writer) { - var f = undefined; -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.settings.v1.GetSettingsResponse.repeatedFields_ = [1]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.settings.v1.GetSettingsResponse.prototype.toObject = function(opt_includeInstance) { - return proto.settings.v1.GetSettingsResponse.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.settings.v1.GetSettingsResponse} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.settings.v1.GetSettingsResponse.toObject = function(includeInstance, msg) { - var f, obj = { - settingsList: jspb.Message.toObjectList(msg.getSettingsList(), - proto.settings.v1.Setting.toObject, includeInstance) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.settings.v1.GetSettingsResponse} - */ -proto.settings.v1.GetSettingsResponse.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.settings.v1.GetSettingsResponse; - return proto.settings.v1.GetSettingsResponse.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.settings.v1.GetSettingsResponse} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.settings.v1.GetSettingsResponse} - */ -proto.settings.v1.GetSettingsResponse.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = new proto.settings.v1.Setting; - reader.readMessage(value,proto.settings.v1.Setting.deserializeBinaryFromReader); - msg.addSettings(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.settings.v1.GetSettingsResponse.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.settings.v1.GetSettingsResponse.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.settings.v1.GetSettingsResponse} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.settings.v1.GetSettingsResponse.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getSettingsList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 1, - f, - proto.settings.v1.Setting.serializeBinaryToWriter - ); - } -}; - - -/** - * repeated Setting settings = 1; - * @return {!Array<!proto.settings.v1.Setting>} - */ -proto.settings.v1.GetSettingsResponse.prototype.getSettingsList = function() { - return /** @type{!Array<!proto.settings.v1.Setting>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.settings.v1.Setting, 1)); -}; - - -/** - * @param {!Array<!proto.settings.v1.Setting>} value - * @return {!proto.settings.v1.GetSettingsResponse} returns this -*/ -proto.settings.v1.GetSettingsResponse.prototype.setSettingsList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 1, value); -}; - - -/** - * @param {!proto.settings.v1.Setting=} opt_value - * @param {number=} opt_index - * @return {!proto.settings.v1.Setting} - */ -proto.settings.v1.GetSettingsResponse.prototype.addSettings = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.settings.v1.Setting, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.settings.v1.GetSettingsResponse} returns this - */ -proto.settings.v1.GetSettingsResponse.prototype.clearSettingsList = function() { - return this.setSettingsList([]); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.settings.v1.SetSettingsRequest.prototype.toObject = function(opt_includeInstance) { - return proto.settings.v1.SetSettingsRequest.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.settings.v1.SetSettingsRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.settings.v1.SetSettingsRequest.toObject = function(includeInstance, msg) { - var f, obj = { - setting: (f = msg.getSetting()) && proto.settings.v1.Setting.toObject(includeInstance, f) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.settings.v1.SetSettingsRequest} - */ -proto.settings.v1.SetSettingsRequest.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.settings.v1.SetSettingsRequest; - return proto.settings.v1.SetSettingsRequest.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.settings.v1.SetSettingsRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.settings.v1.SetSettingsRequest} - */ -proto.settings.v1.SetSettingsRequest.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = new proto.settings.v1.Setting; - reader.readMessage(value,proto.settings.v1.Setting.deserializeBinaryFromReader); - msg.setSetting(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.settings.v1.SetSettingsRequest.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.settings.v1.SetSettingsRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.settings.v1.SetSettingsRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.settings.v1.SetSettingsRequest.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getSetting(); - if (f != null) { - writer.writeMessage( - 1, - f, - proto.settings.v1.Setting.serializeBinaryToWriter - ); - } -}; - - -/** - * optional Setting setting = 1; - * @return {?proto.settings.v1.Setting} - */ -proto.settings.v1.SetSettingsRequest.prototype.getSetting = function() { - return /** @type{?proto.settings.v1.Setting} */ ( - jspb.Message.getWrapperField(this, proto.settings.v1.Setting, 1)); -}; - - -/** - * @param {?proto.settings.v1.Setting|undefined} value - * @return {!proto.settings.v1.SetSettingsRequest} returns this -*/ -proto.settings.v1.SetSettingsRequest.prototype.setSetting = function(value) { - return jspb.Message.setWrapperField(this, 1, value); -}; - - -/** - * Clears the message field making it undefined. - * @return {!proto.settings.v1.SetSettingsRequest} returns this - */ -proto.settings.v1.SetSettingsRequest.prototype.clearSetting = function() { - return this.setSetting(undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.settings.v1.SetSettingsRequest.prototype.hasSetting = function() { - return jspb.Message.getField(this, 1) != null; -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.settings.v1.SetSettingsResponse.prototype.toObject = function(opt_includeInstance) { - return proto.settings.v1.SetSettingsResponse.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.settings.v1.SetSettingsResponse} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.settings.v1.SetSettingsResponse.toObject = function(includeInstance, msg) { - var f, obj = { - setting: (f = msg.getSetting()) && proto.settings.v1.Setting.toObject(includeInstance, f) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.settings.v1.SetSettingsResponse} - */ -proto.settings.v1.SetSettingsResponse.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.settings.v1.SetSettingsResponse; - return proto.settings.v1.SetSettingsResponse.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.settings.v1.SetSettingsResponse} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.settings.v1.SetSettingsResponse} - */ -proto.settings.v1.SetSettingsResponse.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = new proto.settings.v1.Setting; - reader.readMessage(value,proto.settings.v1.Setting.deserializeBinaryFromReader); - msg.setSetting(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.settings.v1.SetSettingsResponse.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.settings.v1.SetSettingsResponse.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.settings.v1.SetSettingsResponse} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.settings.v1.SetSettingsResponse.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getSetting(); - if (f != null) { - writer.writeMessage( - 1, - f, - proto.settings.v1.Setting.serializeBinaryToWriter - ); - } -}; - - -/** - * optional Setting setting = 1; - * @return {?proto.settings.v1.Setting} - */ -proto.settings.v1.SetSettingsResponse.prototype.getSetting = function() { - return /** @type{?proto.settings.v1.Setting} */ ( - jspb.Message.getWrapperField(this, proto.settings.v1.Setting, 1)); -}; - - -/** - * @param {?proto.settings.v1.Setting|undefined} value - * @return {!proto.settings.v1.SetSettingsResponse} returns this -*/ -proto.settings.v1.SetSettingsResponse.prototype.setSetting = function(value) { - return jspb.Message.setWrapperField(this, 1, value); -}; - - -/** - * Clears the message field making it undefined. - * @return {!proto.settings.v1.SetSettingsResponse} returns this - */ -proto.settings.v1.SetSettingsResponse.prototype.clearSetting = function() { - return this.setSetting(undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.settings.v1.SetSettingsResponse.prototype.hasSetting = function() { - return jspb.Message.getField(this, 1) != null; -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.settings.v1.Setting.prototype.toObject = function(opt_includeInstance) { - return proto.settings.v1.Setting.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.settings.v1.Setting} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.settings.v1.Setting.toObject = function(includeInstance, msg) { - var f, obj = { - name: jspb.Message.getFieldWithDefault(msg, 1, ""), - value: jspb.Message.getFieldWithDefault(msg, 2, ""), - modifiedat: jspb.Message.getFieldWithDefault(msg, 3, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.settings.v1.Setting} - */ -proto.settings.v1.Setting.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.settings.v1.Setting; - return proto.settings.v1.Setting.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.settings.v1.Setting} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.settings.v1.Setting} - */ -proto.settings.v1.Setting.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setName(value); - break; - case 2: - var value = /** @type {string} */ (reader.readString()); - msg.setValue(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt64()); - msg.setModifiedat(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.settings.v1.Setting.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.settings.v1.Setting.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.settings.v1.Setting} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.settings.v1.Setting.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getName(); - if (f.length > 0) { - writer.writeString( - 1, - f - ); - } - f = message.getValue(); - if (f.length > 0) { - writer.writeString( - 2, - f - ); - } - f = message.getModifiedat(); - if (f !== 0) { - writer.writeInt64( - 3, - f - ); - } -}; - - -/** - * optional string name = 1; - * @return {string} - */ -proto.settings.v1.Setting.prototype.getName = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - - -/** - * @param {string} value - * @return {!proto.settings.v1.Setting} returns this - */ -proto.settings.v1.Setting.prototype.setName = function(value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - - -/** - * optional string value = 2; - * @return {string} - */ -proto.settings.v1.Setting.prototype.getValue = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); -}; - - -/** - * @param {string} value - * @return {!proto.settings.v1.Setting} returns this - */ -proto.settings.v1.Setting.prototype.setValue = function(value) { - return jspb.Message.setProto3StringField(this, 2, value); -}; - - -/** - * optional int64 modifiedAt = 3; - * @return {number} - */ -proto.settings.v1.Setting.prototype.getModifiedat = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.settings.v1.Setting} returns this - */ -proto.settings.v1.Setting.prototype.setModifiedat = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -goog.object.extend(exports, proto.settings.v1); diff --git a/pyroscope/shared.js b/pyroscope/shared.js deleted file mode 100644 index 380f8f59..00000000 --- a/pyroscope/shared.js +++ /dev/null @@ -1,229 +0,0 @@ -const { QrynBadRequest } = require('../lib/handlers/errors') -const Sql = require('@cloki/clickhouse-sql') -const types = require('./types/v1/types_pb') -/** - * - * @param payload {ReadableStream} - * @returns {Promise<Buffer>} - */ -const bufferize = async (payload) => { - const _body = [] - payload.on('data', data => { - _body.push(data)// += data.toString() - }) - if (payload.isPaused && payload.isPaused()) { - payload.resume() - } - await new Promise(resolve => { - payload.on('end', resolve) - payload.on('close', resolve) - }) - const body = Buffer.concat(_body) - if (body.length === 0) { - return null - } - return body -} - -const parser = (MsgClass) => { - return async (req, payload) => { - const body = await bufferize(payload) - req._rawBody = body - return MsgClass.deserializeBinary(body) - } -} - -/** - * - * @param proto {Object} - */ -const normalizeProtoResponse = (proto) => { - if (typeof proto !== 'object') { - return proto - } - return Object.fromEntries(Object.entries(proto).map((e) => { - let name = e[0] - if (name.endsWith('List')) { - name = name.slice(0, -4) - } - if (Array.isArray(e[1])) { - return [name, e[1].map(normalizeProtoResponse)] - } - if (typeof e[1] === 'object') { - return [name, normalizeProtoResponse(e[1])] - } - return [name, e[1]] - })) -} - -const wrapResponse = (hndl) => { - return async (req, res) => { - const _res = await hndl(req, res) - if (!_res || !_res.serializeBinary) { - return _res - } - if (req.type === 'json') { - const strRes = JSON.stringify(normalizeProtoResponse(_res.toObject())) - return res.code(200).send(strRes) - } - return res.code(200).send(Buffer.from(_res.serializeBinary())) - } -} - -const serviceNameSelectorQuery = (labelSelector) => { - const empty = Sql.Eq(new Sql.Raw('1'), new Sql.Raw('1')) - if (!labelSelector || !labelSelector.length || labelSelector === '{}') { - return empty - } - const labelSelectorScript = parseLabelSelector(labelSelector) - let conds = null - for (const rule of labelSelectorScript) { - const label = rule[0] - if (label !== 'service_name') { - continue - } - const val = JSON.parse(rule[2]) - let valRul = null - switch (rule[1]) { - case '=': - valRul = Sql.Eq(new Sql.Raw('service_name'), Sql.val(val)) - break - case '!=': - valRul = Sql.Ne(new Sql.Raw('service_name'), Sql.val(val)) - break - case '=~': - valRul = Sql.Eq(new Sql.Raw(`match(service_name, ${Sql.quoteVal(val)})`), 1) - break - case '!~': - valRul = Sql.Ne(new Sql.Raw(`match(service_name, ${Sql.quoteVal(val)})`), 1) - } - conds = valRul - } - return conds || empty -} - -/** - * - * @param query {string} - */ -const parseQuery = (query) => { - query = query.trim() - const match = query.match(/^([^{\s]+)\s*(\{(.*)})?$/) - if (!match) { - return null - } - const typeId = match[1] - const typeDesc = parseTypeId(typeId) - const strLabels = (match[3] || '').trim() - const labels = parseLabelSelector(strLabels) - const profileType = new types.ProfileType() - profileType.setId(typeId) - profileType.setName(typeDesc.type) - profileType.setSampleType(typeDesc.sampleType) - profileType.setSampleUnit(typeDesc.sampleUnit) - profileType.setPeriodType(typeDesc.periodType) - profileType.setPeriodUnit(typeDesc.periodUnit) - return { - typeId, - typeDesc, - labels, - labelSelector: strLabels, - profileType - } -} - -const parseLabelSelector = (strLabels) => { - strLabels = strLabels.trim() - if (strLabels.startsWith('{')) { - strLabels = strLabels.slice(1) - } - if (strLabels.endsWith('}')) { - strLabels = strLabels.slice(0, -1) - } - const labels = [] - while (strLabels && strLabels !== '' && strLabels !== '}' && strLabels !== ',') { - const m = strLabels.match(/^(,)?\s*([A-Za-z0-9_]+)\s*(!=|!~|=~|=)\s*("([^"\\]|\\.)*")/) - if (!m) { - throw new Error('Invalid label selector') - } - labels.push([m[2], m[3], m[4]]) - strLabels = strLabels.substring(m[0].length).trim() - } - return labels -} - -/** - * - * @param typeId {string} - */ -const parseTypeId = (typeId) => { - const typeParts = typeId.match(/^([^:]+):([^:]+):([^:]+):([^:]+):([^:]+)$/) - if (!typeParts) { - throw new QrynBadRequest('invalid type id') - } - return { - type: typeParts[1], - sampleType: typeParts[2], - sampleUnit: typeParts[3], - periodType: typeParts[4], - periodUnit: typeParts[5] - } -} - -/** - * - * @param {Sql.Select} query - * @param {string} labelSelector - */ -const labelSelectorQuery = (query, labelSelector) => { - if (!labelSelector || !labelSelector.length || labelSelector === '{}') { - return query - } - const labelSelectorScript = parseLabelSelector(labelSelector) - const labelsConds = [] - for (const rule of labelSelectorScript) { - const val = JSON.parse(rule[2]) - let valRul = null - switch (rule[1]) { - case '=': - valRul = Sql.Eq(new Sql.Raw('val'), Sql.val(val)) - break - case '!=': - valRul = Sql.Ne(new Sql.Raw('val'), Sql.val(val)) - break - case '=~': - valRul = Sql.Eq(new Sql.Raw(`match(val, ${Sql.quoteVal(val)})`), 1) - break - case '!~': - valRul = Sql.Ne(new Sql.Raw(`match(val, ${Sql.quoteVal(val)})`), 1) - } - const labelSubCond = Sql.And( - Sql.Eq('key', Sql.val(rule[0])), - valRul - ) - labelsConds.push(labelSubCond) - } - query.where(Sql.Or(...labelsConds)) - query.groupBy(new Sql.Raw('fingerprint')) - query.having(Sql.Eq( - new Sql.Raw(`groupBitOr(${labelsConds.map((cond, i) => { - return `bitShiftLeft(toUInt64(${cond}), ${i})` - }).join('+')})`), - new Sql.Raw(`bitShiftLeft(toUInt64(1), ${labelsConds.length})-1`) - )) -} - -const HISTORY_TIMESPAN = 1000 * 60 * 60 * 24 * 7 - -module.exports = { - bufferize, - parser, - normalizeProtoResponse, - wrapResponse, - parseTypeId, - serviceNameSelectorQuery, - parseLabelSelector, - labelSelectorQuery, - HISTORY_TIMESPAN, - parseQuery -} diff --git a/pyroscope/types/v1/types_grpc_pb.js b/pyroscope/types/v1/types_grpc_pb.js deleted file mode 100644 index 97b3a246..00000000 --- a/pyroscope/types/v1/types_grpc_pb.js +++ /dev/null @@ -1 +0,0 @@ -// GENERATED CODE -- NO SERVICES IN PROTO \ No newline at end of file diff --git a/pyroscope/types/v1/types_pb.js b/pyroscope/types/v1/types_pb.js deleted file mode 100644 index b24be923..00000000 --- a/pyroscope/types/v1/types_pb.js +++ /dev/null @@ -1,3460 +0,0 @@ -// source: types/v1/types.proto -/** - * @fileoverview - * @enhanceable - * @suppress {missingRequire} reports error on implicit type usages. - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! -/* eslint-disable */ -// @ts-nocheck - -var jspb = require('google-protobuf'); -var goog = jspb; -var global = (function() { - if (this) { return this; } - if (typeof window !== 'undefined') { return window; } - if (typeof global !== 'undefined') { return global; } - if (typeof self !== 'undefined') { return self; } - return Function('return this')(); -}.call(null)); - -goog.exportSymbol('proto.types.v1.BlockCompaction', null, global); -goog.exportSymbol('proto.types.v1.BlockInfo', null, global); -goog.exportSymbol('proto.types.v1.GetProfileStatsRequest', null, global); -goog.exportSymbol('proto.types.v1.GetProfileStatsResponse', null, global); -goog.exportSymbol('proto.types.v1.GoPGO', null, global); -goog.exportSymbol('proto.types.v1.LabelNamesRequest', null, global); -goog.exportSymbol('proto.types.v1.LabelNamesResponse', null, global); -goog.exportSymbol('proto.types.v1.LabelPair', null, global); -goog.exportSymbol('proto.types.v1.LabelValuesRequest', null, global); -goog.exportSymbol('proto.types.v1.LabelValuesResponse', null, global); -goog.exportSymbol('proto.types.v1.Labels', null, global); -goog.exportSymbol('proto.types.v1.Location', null, global); -goog.exportSymbol('proto.types.v1.Point', null, global); -goog.exportSymbol('proto.types.v1.ProfileType', null, global); -goog.exportSymbol('proto.types.v1.Series', null, global); -goog.exportSymbol('proto.types.v1.StackTraceSelector', null, global); -goog.exportSymbol('proto.types.v1.TimeSeriesAggregationType', null, global); -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.types.v1.LabelPair = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.types.v1.LabelPair, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.types.v1.LabelPair.displayName = 'proto.types.v1.LabelPair'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.types.v1.ProfileType = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.types.v1.ProfileType, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.types.v1.ProfileType.displayName = 'proto.types.v1.ProfileType'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.types.v1.Labels = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.types.v1.Labels.repeatedFields_, null); -}; -goog.inherits(proto.types.v1.Labels, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.types.v1.Labels.displayName = 'proto.types.v1.Labels'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.types.v1.Series = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.types.v1.Series.repeatedFields_, null); -}; -goog.inherits(proto.types.v1.Series, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.types.v1.Series.displayName = 'proto.types.v1.Series'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.types.v1.Point = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.types.v1.Point, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.types.v1.Point.displayName = 'proto.types.v1.Point'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.types.v1.LabelValuesRequest = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.types.v1.LabelValuesRequest.repeatedFields_, null); -}; -goog.inherits(proto.types.v1.LabelValuesRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.types.v1.LabelValuesRequest.displayName = 'proto.types.v1.LabelValuesRequest'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.types.v1.LabelValuesResponse = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.types.v1.LabelValuesResponse.repeatedFields_, null); -}; -goog.inherits(proto.types.v1.LabelValuesResponse, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.types.v1.LabelValuesResponse.displayName = 'proto.types.v1.LabelValuesResponse'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.types.v1.LabelNamesRequest = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.types.v1.LabelNamesRequest.repeatedFields_, null); -}; -goog.inherits(proto.types.v1.LabelNamesRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.types.v1.LabelNamesRequest.displayName = 'proto.types.v1.LabelNamesRequest'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.types.v1.LabelNamesResponse = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.types.v1.LabelNamesResponse.repeatedFields_, null); -}; -goog.inherits(proto.types.v1.LabelNamesResponse, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.types.v1.LabelNamesResponse.displayName = 'proto.types.v1.LabelNamesResponse'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.types.v1.BlockInfo = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.types.v1.BlockInfo.repeatedFields_, null); -}; -goog.inherits(proto.types.v1.BlockInfo, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.types.v1.BlockInfo.displayName = 'proto.types.v1.BlockInfo'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.types.v1.BlockCompaction = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.types.v1.BlockCompaction.repeatedFields_, null); -}; -goog.inherits(proto.types.v1.BlockCompaction, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.types.v1.BlockCompaction.displayName = 'proto.types.v1.BlockCompaction'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.types.v1.StackTraceSelector = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.types.v1.StackTraceSelector.repeatedFields_, null); -}; -goog.inherits(proto.types.v1.StackTraceSelector, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.types.v1.StackTraceSelector.displayName = 'proto.types.v1.StackTraceSelector'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.types.v1.Location = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.types.v1.Location, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.types.v1.Location.displayName = 'proto.types.v1.Location'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.types.v1.GoPGO = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.types.v1.GoPGO, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.types.v1.GoPGO.displayName = 'proto.types.v1.GoPGO'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.types.v1.GetProfileStatsRequest = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.types.v1.GetProfileStatsRequest, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.types.v1.GetProfileStatsRequest.displayName = 'proto.types.v1.GetProfileStatsRequest'; -} -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.types.v1.GetProfileStatsResponse = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, null); -}; -goog.inherits(proto.types.v1.GetProfileStatsResponse, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.types.v1.GetProfileStatsResponse.displayName = 'proto.types.v1.GetProfileStatsResponse'; -} - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.types.v1.LabelPair.prototype.toObject = function(opt_includeInstance) { - return proto.types.v1.LabelPair.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.types.v1.LabelPair} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.LabelPair.toObject = function(includeInstance, msg) { - var f, obj = { - name: jspb.Message.getFieldWithDefault(msg, 1, ""), - value: jspb.Message.getFieldWithDefault(msg, 2, "") - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.types.v1.LabelPair} - */ -proto.types.v1.LabelPair.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.types.v1.LabelPair; - return proto.types.v1.LabelPair.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.types.v1.LabelPair} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.types.v1.LabelPair} - */ -proto.types.v1.LabelPair.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setName(value); - break; - case 2: - var value = /** @type {string} */ (reader.readString()); - msg.setValue(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.types.v1.LabelPair.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.types.v1.LabelPair.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.types.v1.LabelPair} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.LabelPair.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getName(); - if (f.length > 0) { - writer.writeString( - 1, - f - ); - } - f = message.getValue(); - if (f.length > 0) { - writer.writeString( - 2, - f - ); - } -}; - - -/** - * optional string name = 1; - * @return {string} - */ -proto.types.v1.LabelPair.prototype.getName = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - - -/** - * @param {string} value - * @return {!proto.types.v1.LabelPair} returns this - */ -proto.types.v1.LabelPair.prototype.setName = function(value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - - -/** - * optional string value = 2; - * @return {string} - */ -proto.types.v1.LabelPair.prototype.getValue = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); -}; - - -/** - * @param {string} value - * @return {!proto.types.v1.LabelPair} returns this - */ -proto.types.v1.LabelPair.prototype.setValue = function(value) { - return jspb.Message.setProto3StringField(this, 2, value); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.types.v1.ProfileType.prototype.toObject = function(opt_includeInstance) { - return proto.types.v1.ProfileType.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.types.v1.ProfileType} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.ProfileType.toObject = function(includeInstance, msg) { - var f, obj = { - id: jspb.Message.getFieldWithDefault(msg, 1, ""), - name: jspb.Message.getFieldWithDefault(msg, 2, ""), - sampleType: jspb.Message.getFieldWithDefault(msg, 4, ""), - sampleUnit: jspb.Message.getFieldWithDefault(msg, 5, ""), - periodType: jspb.Message.getFieldWithDefault(msg, 6, ""), - periodUnit: jspb.Message.getFieldWithDefault(msg, 7, "") - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.types.v1.ProfileType} - */ -proto.types.v1.ProfileType.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.types.v1.ProfileType; - return proto.types.v1.ProfileType.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.types.v1.ProfileType} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.types.v1.ProfileType} - */ -proto.types.v1.ProfileType.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setId(value); - break; - case 2: - var value = /** @type {string} */ (reader.readString()); - msg.setName(value); - break; - case 4: - var value = /** @type {string} */ (reader.readString()); - msg.setSampleType(value); - break; - case 5: - var value = /** @type {string} */ (reader.readString()); - msg.setSampleUnit(value); - break; - case 6: - var value = /** @type {string} */ (reader.readString()); - msg.setPeriodType(value); - break; - case 7: - var value = /** @type {string} */ (reader.readString()); - msg.setPeriodUnit(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.types.v1.ProfileType.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.types.v1.ProfileType.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.types.v1.ProfileType} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.ProfileType.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getId(); - if (f.length > 0) { - writer.writeString( - 1, - f - ); - } - f = message.getName(); - if (f.length > 0) { - writer.writeString( - 2, - f - ); - } - f = message.getSampleType(); - if (f.length > 0) { - writer.writeString( - 4, - f - ); - } - f = message.getSampleUnit(); - if (f.length > 0) { - writer.writeString( - 5, - f - ); - } - f = message.getPeriodType(); - if (f.length > 0) { - writer.writeString( - 6, - f - ); - } - f = message.getPeriodUnit(); - if (f.length > 0) { - writer.writeString( - 7, - f - ); - } -}; - - -/** - * optional string ID = 1; - * @return {string} - */ -proto.types.v1.ProfileType.prototype.getId = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - - -/** - * @param {string} value - * @return {!proto.types.v1.ProfileType} returns this - */ -proto.types.v1.ProfileType.prototype.setId = function(value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - - -/** - * optional string name = 2; - * @return {string} - */ -proto.types.v1.ProfileType.prototype.getName = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); -}; - - -/** - * @param {string} value - * @return {!proto.types.v1.ProfileType} returns this - */ -proto.types.v1.ProfileType.prototype.setName = function(value) { - return jspb.Message.setProto3StringField(this, 2, value); -}; - - -/** - * optional string sample_type = 4; - * @return {string} - */ -proto.types.v1.ProfileType.prototype.getSampleType = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "")); -}; - - -/** - * @param {string} value - * @return {!proto.types.v1.ProfileType} returns this - */ -proto.types.v1.ProfileType.prototype.setSampleType = function(value) { - return jspb.Message.setProto3StringField(this, 4, value); -}; - - -/** - * optional string sample_unit = 5; - * @return {string} - */ -proto.types.v1.ProfileType.prototype.getSampleUnit = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, "")); -}; - - -/** - * @param {string} value - * @return {!proto.types.v1.ProfileType} returns this - */ -proto.types.v1.ProfileType.prototype.setSampleUnit = function(value) { - return jspb.Message.setProto3StringField(this, 5, value); -}; - - -/** - * optional string period_type = 6; - * @return {string} - */ -proto.types.v1.ProfileType.prototype.getPeriodType = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, "")); -}; - - -/** - * @param {string} value - * @return {!proto.types.v1.ProfileType} returns this - */ -proto.types.v1.ProfileType.prototype.setPeriodType = function(value) { - return jspb.Message.setProto3StringField(this, 6, value); -}; - - -/** - * optional string period_unit = 7; - * @return {string} - */ -proto.types.v1.ProfileType.prototype.getPeriodUnit = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 7, "")); -}; - - -/** - * @param {string} value - * @return {!proto.types.v1.ProfileType} returns this - */ -proto.types.v1.ProfileType.prototype.setPeriodUnit = function(value) { - return jspb.Message.setProto3StringField(this, 7, value); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.types.v1.Labels.repeatedFields_ = [1]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.types.v1.Labels.prototype.toObject = function(opt_includeInstance) { - return proto.types.v1.Labels.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.types.v1.Labels} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.Labels.toObject = function(includeInstance, msg) { - var f, obj = { - labelsList: jspb.Message.toObjectList(msg.getLabelsList(), - proto.types.v1.LabelPair.toObject, includeInstance) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.types.v1.Labels} - */ -proto.types.v1.Labels.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.types.v1.Labels; - return proto.types.v1.Labels.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.types.v1.Labels} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.types.v1.Labels} - */ -proto.types.v1.Labels.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = new proto.types.v1.LabelPair; - reader.readMessage(value,proto.types.v1.LabelPair.deserializeBinaryFromReader); - msg.addLabels(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.types.v1.Labels.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.types.v1.Labels.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.types.v1.Labels} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.Labels.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getLabelsList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 1, - f, - proto.types.v1.LabelPair.serializeBinaryToWriter - ); - } -}; - - -/** - * repeated LabelPair labels = 1; - * @return {!Array<!proto.types.v1.LabelPair>} - */ -proto.types.v1.Labels.prototype.getLabelsList = function() { - return /** @type{!Array<!proto.types.v1.LabelPair>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.types.v1.LabelPair, 1)); -}; - - -/** - * @param {!Array<!proto.types.v1.LabelPair>} value - * @return {!proto.types.v1.Labels} returns this -*/ -proto.types.v1.Labels.prototype.setLabelsList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 1, value); -}; - - -/** - * @param {!proto.types.v1.LabelPair=} opt_value - * @param {number=} opt_index - * @return {!proto.types.v1.LabelPair} - */ -proto.types.v1.Labels.prototype.addLabels = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.types.v1.LabelPair, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.types.v1.Labels} returns this - */ -proto.types.v1.Labels.prototype.clearLabelsList = function() { - return this.setLabelsList([]); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.types.v1.Series.repeatedFields_ = [1,2]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.types.v1.Series.prototype.toObject = function(opt_includeInstance) { - return proto.types.v1.Series.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.types.v1.Series} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.Series.toObject = function(includeInstance, msg) { - var f, obj = { - labelsList: jspb.Message.toObjectList(msg.getLabelsList(), - proto.types.v1.LabelPair.toObject, includeInstance), - pointsList: jspb.Message.toObjectList(msg.getPointsList(), - proto.types.v1.Point.toObject, includeInstance) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.types.v1.Series} - */ -proto.types.v1.Series.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.types.v1.Series; - return proto.types.v1.Series.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.types.v1.Series} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.types.v1.Series} - */ -proto.types.v1.Series.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = new proto.types.v1.LabelPair; - reader.readMessage(value,proto.types.v1.LabelPair.deserializeBinaryFromReader); - msg.addLabels(value); - break; - case 2: - var value = new proto.types.v1.Point; - reader.readMessage(value,proto.types.v1.Point.deserializeBinaryFromReader); - msg.addPoints(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.types.v1.Series.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.types.v1.Series.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.types.v1.Series} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.Series.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getLabelsList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 1, - f, - proto.types.v1.LabelPair.serializeBinaryToWriter - ); - } - f = message.getPointsList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 2, - f, - proto.types.v1.Point.serializeBinaryToWriter - ); - } -}; - - -/** - * repeated LabelPair labels = 1; - * @return {!Array<!proto.types.v1.LabelPair>} - */ -proto.types.v1.Series.prototype.getLabelsList = function() { - return /** @type{!Array<!proto.types.v1.LabelPair>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.types.v1.LabelPair, 1)); -}; - - -/** - * @param {!Array<!proto.types.v1.LabelPair>} value - * @return {!proto.types.v1.Series} returns this -*/ -proto.types.v1.Series.prototype.setLabelsList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 1, value); -}; - - -/** - * @param {!proto.types.v1.LabelPair=} opt_value - * @param {number=} opt_index - * @return {!proto.types.v1.LabelPair} - */ -proto.types.v1.Series.prototype.addLabels = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.types.v1.LabelPair, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.types.v1.Series} returns this - */ -proto.types.v1.Series.prototype.clearLabelsList = function() { - return this.setLabelsList([]); -}; - - -/** - * repeated Point points = 2; - * @return {!Array<!proto.types.v1.Point>} - */ -proto.types.v1.Series.prototype.getPointsList = function() { - return /** @type{!Array<!proto.types.v1.Point>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.types.v1.Point, 2)); -}; - - -/** - * @param {!Array<!proto.types.v1.Point>} value - * @return {!proto.types.v1.Series} returns this -*/ -proto.types.v1.Series.prototype.setPointsList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 2, value); -}; - - -/** - * @param {!proto.types.v1.Point=} opt_value - * @param {number=} opt_index - * @return {!proto.types.v1.Point} - */ -proto.types.v1.Series.prototype.addPoints = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 2, opt_value, proto.types.v1.Point, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.types.v1.Series} returns this - */ -proto.types.v1.Series.prototype.clearPointsList = function() { - return this.setPointsList([]); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.types.v1.Point.prototype.toObject = function(opt_includeInstance) { - return proto.types.v1.Point.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.types.v1.Point} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.Point.toObject = function(includeInstance, msg) { - var f, obj = { - value: jspb.Message.getFloatingPointFieldWithDefault(msg, 1, 0.0), - timestamp: jspb.Message.getFieldWithDefault(msg, 2, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.types.v1.Point} - */ -proto.types.v1.Point.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.types.v1.Point; - return proto.types.v1.Point.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.types.v1.Point} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.types.v1.Point} - */ -proto.types.v1.Point.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readDouble()); - msg.setValue(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt64()); - msg.setTimestamp(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.types.v1.Point.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.types.v1.Point.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.types.v1.Point} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.Point.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getValue(); - if (f !== 0.0) { - writer.writeDouble( - 1, - f - ); - } - f = message.getTimestamp(); - if (f !== 0) { - writer.writeInt64( - 2, - f - ); - } -}; - - -/** - * optional double value = 1; - * @return {number} - */ -proto.types.v1.Point.prototype.getValue = function() { - return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 1, 0.0)); -}; - - -/** - * @param {number} value - * @return {!proto.types.v1.Point} returns this - */ -proto.types.v1.Point.prototype.setValue = function(value) { - return jspb.Message.setProto3FloatField(this, 1, value); -}; - - -/** - * optional int64 timestamp = 2; - * @return {number} - */ -proto.types.v1.Point.prototype.getTimestamp = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.types.v1.Point} returns this - */ -proto.types.v1.Point.prototype.setTimestamp = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.types.v1.LabelValuesRequest.repeatedFields_ = [2]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.types.v1.LabelValuesRequest.prototype.toObject = function(opt_includeInstance) { - return proto.types.v1.LabelValuesRequest.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.types.v1.LabelValuesRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.LabelValuesRequest.toObject = function(includeInstance, msg) { - var f, obj = { - name: jspb.Message.getFieldWithDefault(msg, 1, ""), - matchersList: (f = jspb.Message.getRepeatedField(msg, 2)) == null ? undefined : f, - start: jspb.Message.getFieldWithDefault(msg, 3, 0), - end: jspb.Message.getFieldWithDefault(msg, 4, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.types.v1.LabelValuesRequest} - */ -proto.types.v1.LabelValuesRequest.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.types.v1.LabelValuesRequest; - return proto.types.v1.LabelValuesRequest.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.types.v1.LabelValuesRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.types.v1.LabelValuesRequest} - */ -proto.types.v1.LabelValuesRequest.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setName(value); - break; - case 2: - var value = /** @type {string} */ (reader.readString()); - msg.addMatchers(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt64()); - msg.setStart(value); - break; - case 4: - var value = /** @type {number} */ (reader.readInt64()); - msg.setEnd(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.types.v1.LabelValuesRequest.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.types.v1.LabelValuesRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.types.v1.LabelValuesRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.LabelValuesRequest.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getName(); - if (f.length > 0) { - writer.writeString( - 1, - f - ); - } - f = message.getMatchersList(); - if (f.length > 0) { - writer.writeRepeatedString( - 2, - f - ); - } - f = message.getStart(); - if (f !== 0) { - writer.writeInt64( - 3, - f - ); - } - f = message.getEnd(); - if (f !== 0) { - writer.writeInt64( - 4, - f - ); - } -}; - - -/** - * optional string name = 1; - * @return {string} - */ -proto.types.v1.LabelValuesRequest.prototype.getName = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - - -/** - * @param {string} value - * @return {!proto.types.v1.LabelValuesRequest} returns this - */ -proto.types.v1.LabelValuesRequest.prototype.setName = function(value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - - -/** - * repeated string matchers = 2; - * @return {!Array<string>} - */ -proto.types.v1.LabelValuesRequest.prototype.getMatchersList = function() { - return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 2)); -}; - - -/** - * @param {!Array<string>} value - * @return {!proto.types.v1.LabelValuesRequest} returns this - */ -proto.types.v1.LabelValuesRequest.prototype.setMatchersList = function(value) { - return jspb.Message.setField(this, 2, value || []); -}; - - -/** - * @param {string} value - * @param {number=} opt_index - * @return {!proto.types.v1.LabelValuesRequest} returns this - */ -proto.types.v1.LabelValuesRequest.prototype.addMatchers = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 2, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.types.v1.LabelValuesRequest} returns this - */ -proto.types.v1.LabelValuesRequest.prototype.clearMatchersList = function() { - return this.setMatchersList([]); -}; - - -/** - * optional int64 start = 3; - * @return {number} - */ -proto.types.v1.LabelValuesRequest.prototype.getStart = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.types.v1.LabelValuesRequest} returns this - */ -proto.types.v1.LabelValuesRequest.prototype.setStart = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional int64 end = 4; - * @return {number} - */ -proto.types.v1.LabelValuesRequest.prototype.getEnd = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.types.v1.LabelValuesRequest} returns this - */ -proto.types.v1.LabelValuesRequest.prototype.setEnd = function(value) { - return jspb.Message.setProto3IntField(this, 4, value); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.types.v1.LabelValuesResponse.repeatedFields_ = [1]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.types.v1.LabelValuesResponse.prototype.toObject = function(opt_includeInstance) { - return proto.types.v1.LabelValuesResponse.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.types.v1.LabelValuesResponse} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.LabelValuesResponse.toObject = function(includeInstance, msg) { - var f, obj = { - namesList: (f = jspb.Message.getRepeatedField(msg, 1)) == null ? undefined : f - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.types.v1.LabelValuesResponse} - */ -proto.types.v1.LabelValuesResponse.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.types.v1.LabelValuesResponse; - return proto.types.v1.LabelValuesResponse.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.types.v1.LabelValuesResponse} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.types.v1.LabelValuesResponse} - */ -proto.types.v1.LabelValuesResponse.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.addNames(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.types.v1.LabelValuesResponse.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.types.v1.LabelValuesResponse.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.types.v1.LabelValuesResponse} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.LabelValuesResponse.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getNamesList(); - if (f.length > 0) { - writer.writeRepeatedString( - 1, - f - ); - } -}; - - -/** - * repeated string names = 1; - * @return {!Array<string>} - */ -proto.types.v1.LabelValuesResponse.prototype.getNamesList = function() { - return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 1)); -}; - - -/** - * @param {!Array<string>} value - * @return {!proto.types.v1.LabelValuesResponse} returns this - */ -proto.types.v1.LabelValuesResponse.prototype.setNamesList = function(value) { - return jspb.Message.setField(this, 1, value || []); -}; - - -/** - * @param {string} value - * @param {number=} opt_index - * @return {!proto.types.v1.LabelValuesResponse} returns this - */ -proto.types.v1.LabelValuesResponse.prototype.addNames = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 1, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.types.v1.LabelValuesResponse} returns this - */ -proto.types.v1.LabelValuesResponse.prototype.clearNamesList = function() { - return this.setNamesList([]); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.types.v1.LabelNamesRequest.repeatedFields_ = [1]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.types.v1.LabelNamesRequest.prototype.toObject = function(opt_includeInstance) { - return proto.types.v1.LabelNamesRequest.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.types.v1.LabelNamesRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.LabelNamesRequest.toObject = function(includeInstance, msg) { - var f, obj = { - matchersList: (f = jspb.Message.getRepeatedField(msg, 1)) == null ? undefined : f, - start: jspb.Message.getFieldWithDefault(msg, 2, 0), - end: jspb.Message.getFieldWithDefault(msg, 3, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.types.v1.LabelNamesRequest} - */ -proto.types.v1.LabelNamesRequest.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.types.v1.LabelNamesRequest; - return proto.types.v1.LabelNamesRequest.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.types.v1.LabelNamesRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.types.v1.LabelNamesRequest} - */ -proto.types.v1.LabelNamesRequest.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.addMatchers(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt64()); - msg.setStart(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt64()); - msg.setEnd(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.types.v1.LabelNamesRequest.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.types.v1.LabelNamesRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.types.v1.LabelNamesRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.LabelNamesRequest.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getMatchersList(); - if (f.length > 0) { - writer.writeRepeatedString( - 1, - f - ); - } - f = message.getStart(); - if (f !== 0) { - writer.writeInt64( - 2, - f - ); - } - f = message.getEnd(); - if (f !== 0) { - writer.writeInt64( - 3, - f - ); - } -}; - - -/** - * repeated string matchers = 1; - * @return {!Array<string>} - */ -proto.types.v1.LabelNamesRequest.prototype.getMatchersList = function() { - return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 1)); -}; - - -/** - * @param {!Array<string>} value - * @return {!proto.types.v1.LabelNamesRequest} returns this - */ -proto.types.v1.LabelNamesRequest.prototype.setMatchersList = function(value) { - return jspb.Message.setField(this, 1, value || []); -}; - - -/** - * @param {string} value - * @param {number=} opt_index - * @return {!proto.types.v1.LabelNamesRequest} returns this - */ -proto.types.v1.LabelNamesRequest.prototype.addMatchers = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 1, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.types.v1.LabelNamesRequest} returns this - */ -proto.types.v1.LabelNamesRequest.prototype.clearMatchersList = function() { - return this.setMatchersList([]); -}; - - -/** - * optional int64 start = 2; - * @return {number} - */ -proto.types.v1.LabelNamesRequest.prototype.getStart = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.types.v1.LabelNamesRequest} returns this - */ -proto.types.v1.LabelNamesRequest.prototype.setStart = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - -/** - * optional int64 end = 3; - * @return {number} - */ -proto.types.v1.LabelNamesRequest.prototype.getEnd = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.types.v1.LabelNamesRequest} returns this - */ -proto.types.v1.LabelNamesRequest.prototype.setEnd = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.types.v1.LabelNamesResponse.repeatedFields_ = [1]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.types.v1.LabelNamesResponse.prototype.toObject = function(opt_includeInstance) { - return proto.types.v1.LabelNamesResponse.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.types.v1.LabelNamesResponse} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.LabelNamesResponse.toObject = function(includeInstance, msg) { - var f, obj = { - namesList: (f = jspb.Message.getRepeatedField(msg, 1)) == null ? undefined : f - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.types.v1.LabelNamesResponse} - */ -proto.types.v1.LabelNamesResponse.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.types.v1.LabelNamesResponse; - return proto.types.v1.LabelNamesResponse.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.types.v1.LabelNamesResponse} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.types.v1.LabelNamesResponse} - */ -proto.types.v1.LabelNamesResponse.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.addNames(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.types.v1.LabelNamesResponse.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.types.v1.LabelNamesResponse.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.types.v1.LabelNamesResponse} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.LabelNamesResponse.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getNamesList(); - if (f.length > 0) { - writer.writeRepeatedString( - 1, - f - ); - } -}; - - -/** - * repeated string names = 1; - * @return {!Array<string>} - */ -proto.types.v1.LabelNamesResponse.prototype.getNamesList = function() { - return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 1)); -}; - - -/** - * @param {!Array<string>} value - * @return {!proto.types.v1.LabelNamesResponse} returns this - */ -proto.types.v1.LabelNamesResponse.prototype.setNamesList = function(value) { - return jspb.Message.setField(this, 1, value || []); -}; - - -/** - * @param {string} value - * @param {number=} opt_index - * @return {!proto.types.v1.LabelNamesResponse} returns this - */ -proto.types.v1.LabelNamesResponse.prototype.addNames = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 1, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.types.v1.LabelNamesResponse} returns this - */ -proto.types.v1.LabelNamesResponse.prototype.clearNamesList = function() { - return this.setNamesList([]); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.types.v1.BlockInfo.repeatedFields_ = [5]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.types.v1.BlockInfo.prototype.toObject = function(opt_includeInstance) { - return proto.types.v1.BlockInfo.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.types.v1.BlockInfo} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.BlockInfo.toObject = function(includeInstance, msg) { - var f, obj = { - ulid: jspb.Message.getFieldWithDefault(msg, 1, ""), - minTime: jspb.Message.getFieldWithDefault(msg, 2, 0), - maxTime: jspb.Message.getFieldWithDefault(msg, 3, 0), - compaction: (f = msg.getCompaction()) && proto.types.v1.BlockCompaction.toObject(includeInstance, f), - labelsList: jspb.Message.toObjectList(msg.getLabelsList(), - proto.types.v1.LabelPair.toObject, includeInstance) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.types.v1.BlockInfo} - */ -proto.types.v1.BlockInfo.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.types.v1.BlockInfo; - return proto.types.v1.BlockInfo.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.types.v1.BlockInfo} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.types.v1.BlockInfo} - */ -proto.types.v1.BlockInfo.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setUlid(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt64()); - msg.setMinTime(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt64()); - msg.setMaxTime(value); - break; - case 4: - var value = new proto.types.v1.BlockCompaction; - reader.readMessage(value,proto.types.v1.BlockCompaction.deserializeBinaryFromReader); - msg.setCompaction(value); - break; - case 5: - var value = new proto.types.v1.LabelPair; - reader.readMessage(value,proto.types.v1.LabelPair.deserializeBinaryFromReader); - msg.addLabels(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.types.v1.BlockInfo.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.types.v1.BlockInfo.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.types.v1.BlockInfo} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.BlockInfo.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getUlid(); - if (f.length > 0) { - writer.writeString( - 1, - f - ); - } - f = message.getMinTime(); - if (f !== 0) { - writer.writeInt64( - 2, - f - ); - } - f = message.getMaxTime(); - if (f !== 0) { - writer.writeInt64( - 3, - f - ); - } - f = message.getCompaction(); - if (f != null) { - writer.writeMessage( - 4, - f, - proto.types.v1.BlockCompaction.serializeBinaryToWriter - ); - } - f = message.getLabelsList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 5, - f, - proto.types.v1.LabelPair.serializeBinaryToWriter - ); - } -}; - - -/** - * optional string ulid = 1; - * @return {string} - */ -proto.types.v1.BlockInfo.prototype.getUlid = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - - -/** - * @param {string} value - * @return {!proto.types.v1.BlockInfo} returns this - */ -proto.types.v1.BlockInfo.prototype.setUlid = function(value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - - -/** - * optional int64 min_time = 2; - * @return {number} - */ -proto.types.v1.BlockInfo.prototype.getMinTime = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.types.v1.BlockInfo} returns this - */ -proto.types.v1.BlockInfo.prototype.setMinTime = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - -/** - * optional int64 max_time = 3; - * @return {number} - */ -proto.types.v1.BlockInfo.prototype.getMaxTime = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.types.v1.BlockInfo} returns this - */ -proto.types.v1.BlockInfo.prototype.setMaxTime = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * optional BlockCompaction compaction = 4; - * @return {?proto.types.v1.BlockCompaction} - */ -proto.types.v1.BlockInfo.prototype.getCompaction = function() { - return /** @type{?proto.types.v1.BlockCompaction} */ ( - jspb.Message.getWrapperField(this, proto.types.v1.BlockCompaction, 4)); -}; - - -/** - * @param {?proto.types.v1.BlockCompaction|undefined} value - * @return {!proto.types.v1.BlockInfo} returns this -*/ -proto.types.v1.BlockInfo.prototype.setCompaction = function(value) { - return jspb.Message.setWrapperField(this, 4, value); -}; - - -/** - * Clears the message field making it undefined. - * @return {!proto.types.v1.BlockInfo} returns this - */ -proto.types.v1.BlockInfo.prototype.clearCompaction = function() { - return this.setCompaction(undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.types.v1.BlockInfo.prototype.hasCompaction = function() { - return jspb.Message.getField(this, 4) != null; -}; - - -/** - * repeated LabelPair labels = 5; - * @return {!Array<!proto.types.v1.LabelPair>} - */ -proto.types.v1.BlockInfo.prototype.getLabelsList = function() { - return /** @type{!Array<!proto.types.v1.LabelPair>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.types.v1.LabelPair, 5)); -}; - - -/** - * @param {!Array<!proto.types.v1.LabelPair>} value - * @return {!proto.types.v1.BlockInfo} returns this -*/ -proto.types.v1.BlockInfo.prototype.setLabelsList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 5, value); -}; - - -/** - * @param {!proto.types.v1.LabelPair=} opt_value - * @param {number=} opt_index - * @return {!proto.types.v1.LabelPair} - */ -proto.types.v1.BlockInfo.prototype.addLabels = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 5, opt_value, proto.types.v1.LabelPair, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.types.v1.BlockInfo} returns this - */ -proto.types.v1.BlockInfo.prototype.clearLabelsList = function() { - return this.setLabelsList([]); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.types.v1.BlockCompaction.repeatedFields_ = [2,3]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.types.v1.BlockCompaction.prototype.toObject = function(opt_includeInstance) { - return proto.types.v1.BlockCompaction.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.types.v1.BlockCompaction} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.BlockCompaction.toObject = function(includeInstance, msg) { - var f, obj = { - level: jspb.Message.getFieldWithDefault(msg, 1, 0), - sourcesList: (f = jspb.Message.getRepeatedField(msg, 2)) == null ? undefined : f, - parentsList: (f = jspb.Message.getRepeatedField(msg, 3)) == null ? undefined : f - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.types.v1.BlockCompaction} - */ -proto.types.v1.BlockCompaction.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.types.v1.BlockCompaction; - return proto.types.v1.BlockCompaction.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.types.v1.BlockCompaction} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.types.v1.BlockCompaction} - */ -proto.types.v1.BlockCompaction.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readInt32()); - msg.setLevel(value); - break; - case 2: - var value = /** @type {string} */ (reader.readString()); - msg.addSources(value); - break; - case 3: - var value = /** @type {string} */ (reader.readString()); - msg.addParents(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.types.v1.BlockCompaction.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.types.v1.BlockCompaction.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.types.v1.BlockCompaction} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.BlockCompaction.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getLevel(); - if (f !== 0) { - writer.writeInt32( - 1, - f - ); - } - f = message.getSourcesList(); - if (f.length > 0) { - writer.writeRepeatedString( - 2, - f - ); - } - f = message.getParentsList(); - if (f.length > 0) { - writer.writeRepeatedString( - 3, - f - ); - } -}; - - -/** - * optional int32 level = 1; - * @return {number} - */ -proto.types.v1.BlockCompaction.prototype.getLevel = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.types.v1.BlockCompaction} returns this - */ -proto.types.v1.BlockCompaction.prototype.setLevel = function(value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - - -/** - * repeated string sources = 2; - * @return {!Array<string>} - */ -proto.types.v1.BlockCompaction.prototype.getSourcesList = function() { - return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 2)); -}; - - -/** - * @param {!Array<string>} value - * @return {!proto.types.v1.BlockCompaction} returns this - */ -proto.types.v1.BlockCompaction.prototype.setSourcesList = function(value) { - return jspb.Message.setField(this, 2, value || []); -}; - - -/** - * @param {string} value - * @param {number=} opt_index - * @return {!proto.types.v1.BlockCompaction} returns this - */ -proto.types.v1.BlockCompaction.prototype.addSources = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 2, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.types.v1.BlockCompaction} returns this - */ -proto.types.v1.BlockCompaction.prototype.clearSourcesList = function() { - return this.setSourcesList([]); -}; - - -/** - * repeated string parents = 3; - * @return {!Array<string>} - */ -proto.types.v1.BlockCompaction.prototype.getParentsList = function() { - return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 3)); -}; - - -/** - * @param {!Array<string>} value - * @return {!proto.types.v1.BlockCompaction} returns this - */ -proto.types.v1.BlockCompaction.prototype.setParentsList = function(value) { - return jspb.Message.setField(this, 3, value || []); -}; - - -/** - * @param {string} value - * @param {number=} opt_index - * @return {!proto.types.v1.BlockCompaction} returns this - */ -proto.types.v1.BlockCompaction.prototype.addParents = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 3, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.types.v1.BlockCompaction} returns this - */ -proto.types.v1.BlockCompaction.prototype.clearParentsList = function() { - return this.setParentsList([]); -}; - - - -/** - * List of repeated fields within this message type. - * @private {!Array<number>} - * @const - */ -proto.types.v1.StackTraceSelector.repeatedFields_ = [1]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.types.v1.StackTraceSelector.prototype.toObject = function(opt_includeInstance) { - return proto.types.v1.StackTraceSelector.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.types.v1.StackTraceSelector} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.StackTraceSelector.toObject = function(includeInstance, msg) { - var f, obj = { - callSiteList: jspb.Message.toObjectList(msg.getCallSiteList(), - proto.types.v1.Location.toObject, includeInstance), - goPgo: (f = msg.getGoPgo()) && proto.types.v1.GoPGO.toObject(includeInstance, f) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.types.v1.StackTraceSelector} - */ -proto.types.v1.StackTraceSelector.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.types.v1.StackTraceSelector; - return proto.types.v1.StackTraceSelector.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.types.v1.StackTraceSelector} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.types.v1.StackTraceSelector} - */ -proto.types.v1.StackTraceSelector.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = new proto.types.v1.Location; - reader.readMessage(value,proto.types.v1.Location.deserializeBinaryFromReader); - msg.addCallSite(value); - break; - case 2: - var value = new proto.types.v1.GoPGO; - reader.readMessage(value,proto.types.v1.GoPGO.deserializeBinaryFromReader); - msg.setGoPgo(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.types.v1.StackTraceSelector.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.types.v1.StackTraceSelector.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.types.v1.StackTraceSelector} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.StackTraceSelector.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getCallSiteList(); - if (f.length > 0) { - writer.writeRepeatedMessage( - 1, - f, - proto.types.v1.Location.serializeBinaryToWriter - ); - } - f = message.getGoPgo(); - if (f != null) { - writer.writeMessage( - 2, - f, - proto.types.v1.GoPGO.serializeBinaryToWriter - ); - } -}; - - -/** - * repeated Location call_site = 1; - * @return {!Array<!proto.types.v1.Location>} - */ -proto.types.v1.StackTraceSelector.prototype.getCallSiteList = function() { - return /** @type{!Array<!proto.types.v1.Location>} */ ( - jspb.Message.getRepeatedWrapperField(this, proto.types.v1.Location, 1)); -}; - - -/** - * @param {!Array<!proto.types.v1.Location>} value - * @return {!proto.types.v1.StackTraceSelector} returns this -*/ -proto.types.v1.StackTraceSelector.prototype.setCallSiteList = function(value) { - return jspb.Message.setRepeatedWrapperField(this, 1, value); -}; - - -/** - * @param {!proto.types.v1.Location=} opt_value - * @param {number=} opt_index - * @return {!proto.types.v1.Location} - */ -proto.types.v1.StackTraceSelector.prototype.addCallSite = function(opt_value, opt_index) { - return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.types.v1.Location, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.types.v1.StackTraceSelector} returns this - */ -proto.types.v1.StackTraceSelector.prototype.clearCallSiteList = function() { - return this.setCallSiteList([]); -}; - - -/** - * optional GoPGO go_pgo = 2; - * @return {?proto.types.v1.GoPGO} - */ -proto.types.v1.StackTraceSelector.prototype.getGoPgo = function() { - return /** @type{?proto.types.v1.GoPGO} */ ( - jspb.Message.getWrapperField(this, proto.types.v1.GoPGO, 2)); -}; - - -/** - * @param {?proto.types.v1.GoPGO|undefined} value - * @return {!proto.types.v1.StackTraceSelector} returns this -*/ -proto.types.v1.StackTraceSelector.prototype.setGoPgo = function(value) { - return jspb.Message.setWrapperField(this, 2, value); -}; - - -/** - * Clears the message field making it undefined. - * @return {!proto.types.v1.StackTraceSelector} returns this - */ -proto.types.v1.StackTraceSelector.prototype.clearGoPgo = function() { - return this.setGoPgo(undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.types.v1.StackTraceSelector.prototype.hasGoPgo = function() { - return jspb.Message.getField(this, 2) != null; -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.types.v1.Location.prototype.toObject = function(opt_includeInstance) { - return proto.types.v1.Location.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.types.v1.Location} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.Location.toObject = function(includeInstance, msg) { - var f, obj = { - name: jspb.Message.getFieldWithDefault(msg, 1, "") - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.types.v1.Location} - */ -proto.types.v1.Location.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.types.v1.Location; - return proto.types.v1.Location.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.types.v1.Location} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.types.v1.Location} - */ -proto.types.v1.Location.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setName(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.types.v1.Location.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.types.v1.Location.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.types.v1.Location} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.Location.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getName(); - if (f.length > 0) { - writer.writeString( - 1, - f - ); - } -}; - - -/** - * optional string name = 1; - * @return {string} - */ -proto.types.v1.Location.prototype.getName = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - - -/** - * @param {string} value - * @return {!proto.types.v1.Location} returns this - */ -proto.types.v1.Location.prototype.setName = function(value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.types.v1.GoPGO.prototype.toObject = function(opt_includeInstance) { - return proto.types.v1.GoPGO.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.types.v1.GoPGO} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.GoPGO.toObject = function(includeInstance, msg) { - var f, obj = { - keepLocations: jspb.Message.getFieldWithDefault(msg, 1, 0), - aggregateCallees: jspb.Message.getBooleanFieldWithDefault(msg, 2, false) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.types.v1.GoPGO} - */ -proto.types.v1.GoPGO.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.types.v1.GoPGO; - return proto.types.v1.GoPGO.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.types.v1.GoPGO} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.types.v1.GoPGO} - */ -proto.types.v1.GoPGO.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {number} */ (reader.readUint32()); - msg.setKeepLocations(value); - break; - case 2: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setAggregateCallees(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.types.v1.GoPGO.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.types.v1.GoPGO.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.types.v1.GoPGO} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.GoPGO.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getKeepLocations(); - if (f !== 0) { - writer.writeUint32( - 1, - f - ); - } - f = message.getAggregateCallees(); - if (f) { - writer.writeBool( - 2, - f - ); - } -}; - - -/** - * optional uint32 keep_locations = 1; - * @return {number} - */ -proto.types.v1.GoPGO.prototype.getKeepLocations = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.types.v1.GoPGO} returns this - */ -proto.types.v1.GoPGO.prototype.setKeepLocations = function(value) { - return jspb.Message.setProto3IntField(this, 1, value); -}; - - -/** - * optional bool aggregate_callees = 2; - * @return {boolean} - */ -proto.types.v1.GoPGO.prototype.getAggregateCallees = function() { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false)); -}; - - -/** - * @param {boolean} value - * @return {!proto.types.v1.GoPGO} returns this - */ -proto.types.v1.GoPGO.prototype.setAggregateCallees = function(value) { - return jspb.Message.setProto3BooleanField(this, 2, value); -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.types.v1.GetProfileStatsRequest.prototype.toObject = function(opt_includeInstance) { - return proto.types.v1.GetProfileStatsRequest.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.types.v1.GetProfileStatsRequest} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.GetProfileStatsRequest.toObject = function(includeInstance, msg) { - var f, obj = { - - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.types.v1.GetProfileStatsRequest} - */ -proto.types.v1.GetProfileStatsRequest.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.types.v1.GetProfileStatsRequest; - return proto.types.v1.GetProfileStatsRequest.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.types.v1.GetProfileStatsRequest} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.types.v1.GetProfileStatsRequest} - */ -proto.types.v1.GetProfileStatsRequest.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.types.v1.GetProfileStatsRequest.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.types.v1.GetProfileStatsRequest.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.types.v1.GetProfileStatsRequest} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.GetProfileStatsRequest.serializeBinaryToWriter = function(message, writer) { - var f = undefined; -}; - - - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.types.v1.GetProfileStatsResponse.prototype.toObject = function(opt_includeInstance) { - return proto.types.v1.GetProfileStatsResponse.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.types.v1.GetProfileStatsResponse} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.GetProfileStatsResponse.toObject = function(includeInstance, msg) { - var f, obj = { - dataIngested: jspb.Message.getBooleanFieldWithDefault(msg, 1, false), - oldestProfileTime: jspb.Message.getFieldWithDefault(msg, 2, 0), - newestProfileTime: jspb.Message.getFieldWithDefault(msg, 3, 0) - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.types.v1.GetProfileStatsResponse} - */ -proto.types.v1.GetProfileStatsResponse.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.types.v1.GetProfileStatsResponse; - return proto.types.v1.GetProfileStatsResponse.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.types.v1.GetProfileStatsResponse} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.types.v1.GetProfileStatsResponse} - */ -proto.types.v1.GetProfileStatsResponse.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {boolean} */ (reader.readBool()); - msg.setDataIngested(value); - break; - case 2: - var value = /** @type {number} */ (reader.readInt64()); - msg.setOldestProfileTime(value); - break; - case 3: - var value = /** @type {number} */ (reader.readInt64()); - msg.setNewestProfileTime(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.types.v1.GetProfileStatsResponse.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.types.v1.GetProfileStatsResponse.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.types.v1.GetProfileStatsResponse} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.types.v1.GetProfileStatsResponse.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getDataIngested(); - if (f) { - writer.writeBool( - 1, - f - ); - } - f = message.getOldestProfileTime(); - if (f !== 0) { - writer.writeInt64( - 2, - f - ); - } - f = message.getNewestProfileTime(); - if (f !== 0) { - writer.writeInt64( - 3, - f - ); - } -}; - - -/** - * optional bool data_ingested = 1; - * @return {boolean} - */ -proto.types.v1.GetProfileStatsResponse.prototype.getDataIngested = function() { - return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 1, false)); -}; - - -/** - * @param {boolean} value - * @return {!proto.types.v1.GetProfileStatsResponse} returns this - */ -proto.types.v1.GetProfileStatsResponse.prototype.setDataIngested = function(value) { - return jspb.Message.setProto3BooleanField(this, 1, value); -}; - - -/** - * optional int64 oldest_profile_time = 2; - * @return {number} - */ -proto.types.v1.GetProfileStatsResponse.prototype.getOldestProfileTime = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.types.v1.GetProfileStatsResponse} returns this - */ -proto.types.v1.GetProfileStatsResponse.prototype.setOldestProfileTime = function(value) { - return jspb.Message.setProto3IntField(this, 2, value); -}; - - -/** - * optional int64 newest_profile_time = 3; - * @return {number} - */ -proto.types.v1.GetProfileStatsResponse.prototype.getNewestProfileTime = function() { - return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); -}; - - -/** - * @param {number} value - * @return {!proto.types.v1.GetProfileStatsResponse} returns this - */ -proto.types.v1.GetProfileStatsResponse.prototype.setNewestProfileTime = function(value) { - return jspb.Message.setProto3IntField(this, 3, value); -}; - - -/** - * @enum {number} - */ -proto.types.v1.TimeSeriesAggregationType = { - TIME_SERIES_AGGREGATION_TYPE_SUM: 0, - TIME_SERIES_AGGREGATION_TYPE_AVERAGE: 1 -}; - -goog.object.extend(exports, proto.types.v1); diff --git a/qryn.mjs b/qryn.mjs deleted file mode 100644 index 0e640572..00000000 --- a/qryn.mjs +++ /dev/null @@ -1,15 +0,0 @@ -/* - * qryn: polyglot observability API - * (C) 2018-2024 QXIP BV - */ - -import {init} from './qryn_node_wrapper.js' -import {bun} from './common.js' -import bunInit from './qryn_bun.mjs' - - -if (bun()) { - bunInit() -} else { - init() -} diff --git a/qryn_bun.mjs b/qryn_bun.mjs deleted file mode 100644 index b972e45e..00000000 --- a/qryn_bun.mjs +++ /dev/null @@ -1,359 +0,0 @@ -#!/usr/bin/env bun - -/* - * qryn: polyglot observability API - * (C) 2018-2024 QXIP BV - */ - -import { Router } from '@stricjs/router'; -import { wrapper, wsWrapper } from './lib/bun_wrapper.js'; - -import { - combinedParser, - jsonParser, - lokiPushJSONParser, - lokiPushProtoParser, otlpLogsDataParser, otlpPushProtoParser, prometheusPushProtoParser, - rawStringParser, - tempoPushNDJSONParser, - tempoPushParser, wwwFormParser, yamlParser -} from './parsers.js' -import handlerPush from './lib/handlers/push.js' -import handle404 from './lib/handlers/404.js' -import handlerHello from './lib/handlers/ready.js' -import handlerElasticPush from './lib/handlers/elastic_index.js' -import handlerElasticBulk from './lib/handlers/elastic_bulk.js' -import handlerTempoPush from './lib/handlers/tempo_push.js' -import handlerTempoTraces from './lib/handlers/tempo_traces.js' -import handlerTempoLabel from './lib/handlers/tempo_tags.js' -import handlerTempoLabelValues from './lib/handlers/tempo_values.js' -import handlerTempoSearch from './lib/handlers/tempo_search.js' -import handlerTempoEcho from './lib/handlers/echo.js' -import handlerTelegraf from './lib/handlers/telegraf.js' -import handlerDatadogLogPush from './lib/handlers/datadog_log_push.js' -import handlerDatadogSeriesPush from './lib/handlers/datadog_series_push.js' -import handlerQueryRange from './lib/handlers/query_range.js' -import handlerQuery from './lib/handlers/query.js' -import handlerLabel from './lib/handlers/label.js' -import handlerLabelValues from './lib/handlers/label_values.js' -import handlerSeries from './lib/handlers/series.js' -import handlerPromSeries from './lib/handlers/prom_series.js' -import promWriteHandler from './lib/handlers/prom_push.js' -import handlerPromQueryRange from './lib/handlers/prom_query_range.js' -import handlerPromQuery from './lib/handlers/prom_query.js' -import handlerPromLabel from './lib/handlers/promlabel.js' -import handlerPromLabelValues from './lib/handlers/promlabel_values.js' -import handlerPromDefault from './lib/handlers/prom_default.js' -import handlerNewrelicLogPush from './lib/handlers/newrelic_log_push.js' -import handlerInfluxWrite from './lib/handlers/influx_write.js' -import handlerInfluxHealth from './lib/handlers/influx_health.js' -import handlerOTLPPush from './lib/handlers/otlp_push.js' -import handlerGetRules from './lib/handlers/alerts/get_rules.js' -import handlerGetGroup from './lib/handlers/alerts/get_group.js' -import handlerPostGroup from './lib/handlers/alerts/post_group.js' -import handlerDelGroup from './lib/handlers/alerts/del_group.js' -import handlerDelNS from './lib/handlers/alerts/del_ns.js' -import handlerPromGetRules from './lib/handlers/alerts/prom_get_rules.js' -import handlerTail from './lib/handlers/tail.js' -import handlerTempoLabelV2 from './lib/handlers/tempo_v2_tags.js' -import handlerTempoLabelV2Values from './lib/handlers/tempo_v2_values.js' -import handlerOtlpLogsPush from './lib/handlers/otlp_log_push.js' -import {init as pyroscopeInit } from './pyroscope/pyroscope.js' - -import { boolEnv, readonly, readerMode, writerMode } from './common.js' -import DATABASE, { init } from './lib/db/clickhouse.js' -import { startAlerting } from './lib/db/alerting/index.js' -import fs from 'fs' -import path from 'path' -import { file, dir, group, CORS } from '@stricjs/utils'; -import auth from 'basic-auth' -import * as errors from 'http-errors' -import logger from './lib/logger.js' - -const http_user = process.env.QRYN_LOGIN || process.env.CLOKI_LOGIN || undefined -const http_password = process.env.QRYN_PASSWORD || process.env.CLOKI_PASSWORD || undefined - -export default async() => { - try { - await init(process.env.CLICKHOUSE_DB || 'cloki') - if (process.env.MODE === 'init_only') { - process.exit(0) - } - } catch (err) { - logger.error(err, 'Error starting qryn') - process.exit(1) - } - if (!readonly) { - await startAlerting() - } - await DATABASE.checkDB() - - const app = new Router() - - const cors = process.env.CORS_ALLOW_ORIGIN || '*' - - app.wrap('/', (resp) => { - const _cors = new CORS({allowOrigins: cors}) - for(const c of Object.entries(_cors.headers)) { - resp.headers.append(c[0], c[1]) - } - return resp - }) - - app.guard("/", (ctx) => { - if (http_user) { - const creds = auth({ headers: Object.fromEntries(ctx.headers.entries()) }) - if (!creds || creds.name !== http_user || creds.pass !== http_password) { - ctx.error = new errors.Unauthorized('Unauthorized') - return null; - } - } - return ctx; - }); - - app.get('/hello', wrapper(handlerHello)) - app.get('/ready', wrapper(handlerHello)) - writerMode && app.post('/loki/api/v1/push', wrapper(handlerPush, { - 'application/json': lokiPushJSONParser, - 'application/x-protobuf': lokiPushProtoParser, - '*': lokiPushJSONParser - })) - writerMode && app.post('/:target/_doc', wrapper(handlerElasticPush, { - 'application/json': jsonParser, - '*': rawStringParser - })) - writerMode && app.post('/:target/_create/:id', wrapper(handlerElasticPush, { - 'application/json': jsonParser, - '*': rawStringParser - })) - writerMode && app.put('/:target/_doc/:id', wrapper(handlerElasticPush, { - 'application/json': jsonParser, - '*': rawStringParser - })) - writerMode && app.put('/:target/_create/:id', wrapper(handlerElasticPush, { - 'application/json': jsonParser, - '*': rawStringParser - })) - writerMode && app.post('/_bulk', wrapper(handlerElasticBulk, { - 'application/json': jsonParser, - '*': rawStringParser - })) - writerMode && app.post('/:target/_bulk', wrapper(handlerElasticBulk, { - 'application/json': jsonParser, - '*': rawStringParser - })) - writerMode && app.post('/tempo/api/push', wrapper(handlerTempoPush, { - 'application/json': tempoPushParser, - 'application/x-ndjson': tempoPushNDJSONParser, - '*': tempoPushParser - })) - writerMode && app.post('/tempo/spans', wrapper(handlerTempoPush, { - 'application/json': tempoPushParser, - 'application/x-ndjson': tempoPushNDJSONParser, - '*': tempoPushParser - })) - writerMode && app.post('/api/v2/spans', wrapper(handlerTempoPush, { - 'application/json': tempoPushParser, - 'application/x-ndjson': tempoPushNDJSONParser, - '*': tempoPushParser - })) - readerMode && app.get('/api/traces/:traceId', wrapper(handlerTempoTraces)) - readerMode && app.get('/api/traces/:traceId/:json', wrapper(handlerTempoTraces)) - readerMode && app.get('/tempo/api/traces/:traceId', wrapper(handlerTempoTraces)) - readerMode && app.get('/tempo/api/traces/:traceId/:json', wrapper(handlerTempoTraces)) - readerMode && app.get('/api/echo', wrapper(handlerTempoEcho)) - readerMode && app.get('/tempo/api/echo', wrapper(handlerTempoEcho)) - readerMode && app.ws('/loki/api/v1/tail', wsWrapper(handlerTail)) - app.get('/config', () => new Response('not supported')) - app.get('/metrics', () => new Response('not supported')) - app.get('/influx/api/v2/write/health', () => new Response('ok')) - - - const fastify = { - get: (path, hndl, parsers) => { - app.get(path, wrapper(hndl, parsers)) - }, - post: (path, hndl, parsers) => { - app.post(path, wrapper(hndl, parsers)) - }, - put: (path, hndl, parsers) => { - app.put(path, wrapper(hndl, parsers)) - }, - delete: (path, hndl, parsers) => { - app.delete(path, wrapper(hndl, parsers)) - } - } - - readerMode && fastify.get('/api/search/tags', handlerTempoLabel) - readerMode && fastify.get('/tempo/api/search/tags', handlerTempoLabel) - - /* Tempo Tag Value Handler */ - readerMode && fastify.get('/api/search/tag/:name/values', handlerTempoLabelValues) - readerMode && fastify.get('/tempo/api/search/tag/:name/values', handlerTempoLabelValues) - - /* Tempo Traces Query Handler */ - readerMode && fastify.get('/api/search', handlerTempoSearch) - readerMode && fastify.get('/tempo/api/search', handlerTempoSearch) - - /* Tempo Echo Handler */ - fastify.get('/api/echo', handlerTempoEcho) - fastify.get('/tempo/api/echo', handlerTempoEcho) - - /* Telegraf HTTP Bulk handler */ - writerMode && fastify.post('/telegraf', handlerTelegraf, { - '*': jsonParser - }) - - /* Datadog Log Push Handler */ - writerMode && fastify.post('/api/v2/logs', handlerDatadogLogPush, { - 'application/json': jsonParser, - '*': rawStringParser - }) - - /* Datadog Series Push Handler */ - - writerMode && fastify.post('/api/v2/series', handlerDatadogSeriesPush, { - 'application/json': jsonParser, - '*': rawStringParser - }) - - /* Query Handler */ - - readerMode && fastify.get('/loki/api/v1/query_range', handlerQueryRange) - - /* Label Handlers */ - /* Label Value Handler via query (test) */ - - readerMode && fastify.get('/loki/api/v1/query', handlerQuery) - - /* Label Handlers */ - readerMode && fastify.get('/loki/api/v1/label', handlerLabel) - readerMode && fastify.get('/loki/api/v1/labels', handlerLabel) - - /* Label Value Handler */ - - readerMode && fastify.get('/loki/api/v1/label/:name/values', handlerLabelValues) - - /* Series Handler - experimental support for both Loki and Prometheus */ - - readerMode && fastify.get('/loki/api/v1/series', handlerSeries) - - readerMode && fastify.get('/api/v1/series', handlerPromSeries) - readerMode && fastify.post('/api/v1/series', handlerPromSeries, { - 'application/x-www-form-urlencoded': wwwFormParser - }) - - /* ALERT MANAGER Handlers */ - readerMode && fastify.get('/api/prom/rules', handlerGetRules) - readerMode && fastify.get('/api/prom/rules/:ns/:group', handlerGetGroup) - readerMode && fastify.post('/api/prom/rules/:ns', handlerPostGroup, { - '*': yamlParser - }) - readerMode && fastify.delete('/api/prom/rules/:ns/:group', handlerDelGroup) - readerMode && fastify.delete('/api/prom/rules/:ns', handlerDelNS) - readerMode && fastify.get('/prometheus/api/v1/rules', handlerPromGetRules) - - /* PROMETHEUS REMOTE WRITE Handlers */ - const remoteWritePaths = [ - '/api/v1/prom/remote/write', - '/api/prom/remote/write', - '/prom/remote/write', - '/api/v1/write', - '/api/prom/push' - ] - for (const path of remoteWritePaths) { - writerMode && fastify.post(path, promWriteHandler, { - 'application/x-protobuf': prometheusPushProtoParser, - 'application/json': jsonParser, - '*': combinedParser(prometheusPushProtoParser, jsonParser) - }) - writerMode && fastify.get(path, handlerTempoEcho) - } - - /* PROMQETHEUS API EMULATION */ - - readerMode && fastify.post('/api/v1/query_range', handlerPromQueryRange, { - 'application/x-www-form-urlencoded': wwwFormParser - }) - readerMode && fastify.get('/api/v1/query_range', handlerPromQueryRange) - - readerMode && fastify.post('/api/v1/query', handlerPromQuery, { - 'application/x-www-form-urlencoded': wwwFormParser - }) - readerMode && fastify.get('/api/v1/query', handlerPromQuery) - readerMode && fastify.get('/api/v1/labels', handlerPromLabel) // piggyback on qryn labels - readerMode && fastify.get('/api/v1/label/:name/values', handlerPromLabelValues) // piggyback on qryn values - readerMode && fastify.post('/api/v1/labels', handlerPromLabel, { - '*': rawStringParser - }) // piggyback on qryn labels - readerMode && fastify.post('/api/v1/label/:name/values', handlerPromLabelValues, { - '*': rawStringParser - }) // piggyback on qryn values - - readerMode && fastify.get('/api/v1/metadata', handlerPromDefault.misc) // default handler TBD - readerMode && fastify.get('/api/v1/rules', handlerPromDefault.rules) // default handler TBD - readerMode && fastify.get('/api/v1/query_exemplars', handlerPromDefault.misc) // default handler TBD - readerMode && fastify.post('/api/v1/query_exemplars', handlerPromDefault.misc, { - 'application/x-www-form-urlencoded': wwwFormParser - }) // default handler TBD - readerMode && fastify.get('/api/v1/format_query', handlerPromDefault.misc) // default handler TBD - readerMode && fastify.post('/api/v1/format_query', handlerPromDefault.misc, { - 'application/x-www-form-urlencoded': wwwFormParser - }) // default handler TBD - fastify.get('/api/v1/status/buildinfo', handlerPromDefault.buildinfo) // default handler TBD - - /* NewRelic Log Handler */ - - writerMode && fastify.post('/log/v1', handlerNewrelicLogPush, { - 'text/plain': jsonParser, - '*': jsonParser - }) - - /* INFLUX WRITE Handlers */ - - writerMode && fastify.post('/write', handlerInfluxWrite, { - '*': rawStringParser - }) - writerMode && fastify.post('/influx/api/v2/write', handlerInfluxWrite, { - '*': rawStringParser - }) - /* INFLUX HEALTH Handlers */ - - fastify.get('/health', handlerInfluxHealth) - fastify.get('/influx/health', handlerInfluxHealth) - - - writerMode && fastify.post('/v1/traces', handlerOTLPPush, { - '*': otlpPushProtoParser - }) - - readerMode && fastify.get('/api/v2/search/tags', handlerTempoLabelV2) - readerMode && fastify.get('/tempo/api/v2/search/tags', handlerTempoLabelV2) - readerMode && fastify.get('/api/v2/search/tag/:name/values', handlerTempoLabelV2Values) - readerMode && fastify.get('/tempo/api/v2/search/tag/:name/values', handlerTempoLabelV2Values) - - readerMode && pyroscopeInit(fastify) - - writerMode && fastify.post('/v1/logs', handlerOtlpLogsPush, { - '*': otlpLogsDataParser - }) - - const serveView = fs.existsSync(path.join(__dirname, 'view/index.html')) - if (serveView) { - app.plug(group(path.join(__dirname, 'view'))); - for (const fakePath of ['/plugins', '/users', '/datasources', '/datasources/:ds']) { - app.get(fakePath, - (ctx) => - file(path.join(__dirname, 'view', 'index.html'))(ctx)) - } - } - - app.use(404, (ctx) => { - if (ctx.error && ctx.error.name === 'UnauthorizedError') { - return new Response(ctx.error.message, {status: 401, headers: { 'www-authenticate': 'Basic' }}) - } - return wrapper(handle404) - }) - app.port = process.env.PORT || 3100 - app.hostname = process.env.HOST || '0.0.0.0' - app.listen() -} diff --git a/qryn_node.js b/qryn_node.js deleted file mode 100755 index 01c8e71b..00000000 --- a/qryn_node.js +++ /dev/null @@ -1,491 +0,0 @@ -#!/usr/bin/env node - -/* - * qryn: polyglot observability API - * (C) 2018-2024 QXIP BV - */ -const { boolEnv, readerMode, writerMode } = require('./common') -const { Duplex } = require('stream') - -this.readonly = boolEnv('READONLY') -this.http_user = process.env.QRYN_LOGIN || process.env.CLOKI_LOGIN || undefined -this.http_password = process.env.QRYN_PASSWORD || process.env.CLOKI_PASSWORD || undefined - -this.maxListeners = process.env.MAXLISTENERS || 0; - -process.setMaxListeners(this.maxListeners) - -require('./plugins/engine') - -const DATABASE = require('./lib/db/clickhouse') -const UTILS = require('./lib/utils') - -/* ProtoBuf Helpers */ -const fs = require('fs') -const path = require('path') - -const logger = require('./lib/logger') - -/* Alerting */ -const { startAlerting, stop } = require('./lib/db/alerting') - -/* Fingerprinting */ -this.fingerPrint = UTILS.fingerPrint -this.toJSON = UTILS.toJSON - -/* Database this.bulk Helpers */ -this.bulk = DATABASE.cache.bulk // samples -this.bulk_labels = DATABASE.cache.bulk_labels // labels -this.labels = DATABASE.cache.labels // in-memory labels - -/* Function Helpers */ -this.labelParser = UTILS.labelParser - -const init = DATABASE.init -this.reloadFingerprints = DATABASE.reloadFingerprints -this.scanFingerprints = DATABASE.scanFingerprints -this.scanTempo = DATABASE.scanTempo -this.instantQueryScan = DATABASE.instantQueryScan -this.tempoQueryScan = DATABASE.tempoQueryScan -this.scanMetricFingerprints = DATABASE.scanMetricFingerprints -this.tempoQueryScan = DATABASE.tempoQueryScan -this.scanClickhouse = DATABASE.scanClickhouse -this.pushZipkin = DATABASE.pushZipkin -this.pushOTLP = DATABASE.pushOTLP -this.queryTempoTags = DATABASE.queryTempoTags -this.queryTempoValues = DATABASE.queryTempoValues -let profiler = null -const pako = require('pako') - -const { - shaper, - parsers, - lokiPushJSONParser, lokiPushProtoParser, jsonParser, rawStringParser, tempoPushParser, tempoPushNDJSONParser, - yamlParser, prometheusPushProtoParser, combinedParser, otlpPushProtoParser, wwwFormParser, otlpLogsDataParser -} = require('./parsers') - -const fastifyPlugin = require('fastify-plugin') - -let fastify = require('fastify')({ - logger, - bodyLimit: parseInt(process.env.FASTIFY_BODYLIMIT) || 5242880, - requestTimeout: parseInt(process.env.FASTIFY_REQUESTTIMEOUT) || 0, - maxRequestsPerSocket: parseInt(process.env.FASTIFY_MAXREQUESTS) || 0 -}); -(async () => { - try { - await init(process.env.CLICKHOUSE_DB || 'cloki') - if (process.env.MODE === 'init_only') { - process.exit(0) - } - } catch (err) { - logger.error(err, 'Error starting qryn') - process.exit(1) - } - try { - if (!this.readonly) { - await startAlerting() - } - await DATABASE.checkDB() - if (!this.readonly && process.env.PROFILE) { - const tag = JSON.stringify({ profiler_id: process.env.PROFILE, label: 'RAM usage' }) - const fp = this.fingerPrint(tag) - profiler = setInterval(() => { - this.bulk_labels.add([[new Date().toISOString().split('T')[0], fp, tag, '']]) - this.bulk.add([[fp, - [['label', 'RAM usage'], ['profiler_id', process.env.PROFILE]], - BigInt(Date.now()) * BigInt(1000000), - process.memoryUsage().rss / 1024 / 1024, '' - ]]) - }, 1000) - } - } catch (err) { - logger.error(err, 'Error starting qryn') - process.exit(1) - } - - await fastify.register(fastifyPlugin((fastify, opts, done) => { - const snappyPaths = [ - '/api/v1/prom/remote/write', - '/api/prom/remote/write', - '/prom/remote/write', - '/loki/api/v1/push', - '/api/v1/write', - '/api/prom/push' - ] - fastify.addHook('preParsing', (request, reply, payload, done) => { - if (snappyPaths.indexOf(request.routeOptions.url) !== -1) { - if (request.headers['content-encoding'] === 'snappy') { - delete request.headers['content-encoding'] - } - } - done(null, payload) - }) - done() - })) - await fastify.register(require('@fastify/compress'), { - encodings: ['gzip'] - }) - await fastify.register(require('@fastify/url-data')) - await fastify.register(require('@fastify/websocket')) - - /* Fastify local metrics exporter */ - if (boolEnv('FASTIFY_METRICS')) { - const metricsPlugin = require('fastify-metrics') - fastify.register(metricsPlugin, { endpoint: '/metrics' }) - } else { - fastify.get('/metrics', () => 'not supported') - } - fastify.get('/config', () => 'not supported') - fastify.get('/influx/api/v2/write/health', () => 'ok') - /* CORS Helper */ - const CORS = process.env.CORS_ALLOW_ORIGIN || '*' - fastify.register(require('@fastify/cors'), { - origin: CORS - }) - - fastify.after((err) => { - if (err) { - logger.error({ err }, 'Error creating http response') - throw err - } - }) - - fastify.__post = fastify.post - fastify.post = (route, handler, _parsers) => { - if (_parsers) { - for (const t of Object.keys(_parsers)) { - parsers.register('post', route, t, _parsers[t]) - } - } - return fastify.__post(route, handler) - } - - fastify.__put = fastify.put - fastify.put = (route, handler, _parsers) => { - const __parsers = handler.parsers || _parsers - if (__parsers) { - for (const t of Object.keys(__parsers)) { - parsers.register('put', route, t, __parsers[t]) - } - } - return fastify.__put(route, handler) - } - - fastify.__all = fastify.all - fastify.all = (route, handler, _parsers) => { - const __parsers = handler.parsers || _parsers - if (__parsers) { - for (const t of Object.keys(__parsers)) { - parsers.register('post', route, t, __parsers[t]) - parsers.register('put', route, t, __parsers[t]) - } - } - return fastify.__all(route, handler) - } - - /* Enable Simple Authentication */ - if (this.http_user && this.http_password) { - function checkAuth (username, password, req, reply, done) { - if (username === this.http_user && password === this.http_password) { - done() - } else { - done(new (require('http-errors').Unauthorized)('Unauthorized!: Wrong username/password.')) - } - } - - const validate = checkAuth.bind(this) - - fastify.register(require('@fastify/basic-auth'), { - validate, - authenticate: true - }) - fastify.after(() => { - fastify.addHook('preHandler', fastify.basicAuth) - }) - } - - /* 404 Handler */ - const handler404 = require('./lib/handlers/404.js').bind(this) - fastify.setNotFoundHandler(handler404) - fastify.setErrorHandler(require('./lib/handlers/errors').handler.bind(this)) - - /* Hello qryn test API */ - const handlerHello = require('./lib/handlers/ready').bind(this) - fastify.get('/hello', handlerHello) - fastify.get('/ready', handlerHello) - - /* Write Handler */ - const handlerPush = require('./lib/handlers/push.js').bind(this) - writerMode && fastify.post('/loki/api/v1/push', handlerPush, { - 'application/json': lokiPushJSONParser, - 'application/x-protobuf': lokiPushProtoParser, - '*': lokiPushJSONParser - }) - - /* Elastic Write Handler */ - const handlerElasticPush = require('./lib/handlers/elastic_index.js').bind(this) - writerMode && fastify.post('/:target/_doc', handlerElasticPush, { - 'application/json': jsonParser, - '*': rawStringParser - }) - writerMode && fastify.post('/:target/_create/:id', handlerElasticPush, { - 'application/json': jsonParser, - '*': rawStringParser - }) - writerMode && fastify.put('/:target/_doc/:id', handlerElasticPush, { - 'application/json': jsonParser, - '*': rawStringParser - }) - writerMode && fastify.put('/:target/_create/:id', handlerElasticPush, { - 'application/json': jsonParser, - '*': rawStringParser - }) - const handlerElasticBulk = require('./lib/handlers/elastic_bulk.js').bind(this) - writerMode && fastify.post('/_bulk', handlerElasticBulk, { - 'application/json': jsonParser, - '*': rawStringParser - }) - writerMode && fastify.post('/:target/_bulk', handlerElasticBulk, { - 'application/json': jsonParser, - '*': rawStringParser - }) - - /* Tempo Write Handler */ - this.tempo_tagtrace = boolEnv('TEMPO_TAGTRACE') - const handlerTempoPush = require('./lib/handlers/tempo_push.js').bind(this) - writerMode && fastify.post('/tempo/api/push', handlerTempoPush, { - 'application/json': tempoPushParser, - 'application/x-ndjson': tempoPushNDJSONParser, - '*': tempoPushParser - }) - writerMode && fastify.post('/tempo/spans', handlerTempoPush, { - 'application/json': tempoPushParser, - 'application/x-ndjson': tempoPushNDJSONParser, - '*': tempoPushParser - }) - writerMode && fastify.post('/api/v2/spans', handlerTempoPush, { - 'application/json': tempoPushParser, - 'application/x-ndjson': tempoPushNDJSONParser, - '*': tempoPushParser - }) - - /* Tempo Traces Query Handler */ - this.tempo_span = process.env.TEMPO_SPAN || 24 - const handlerTempoTraces = require('./lib/handlers/tempo_traces.js').bind(this) - readerMode && fastify.get('/api/traces/:traceId', handlerTempoTraces) - readerMode && fastify.get('/api/traces/:traceId/:json', handlerTempoTraces) - readerMode && fastify.get('/tempo/api/traces/:traceId', handlerTempoTraces) - readerMode && fastify.get('/tempo/api/traces/:traceId/:json', handlerTempoTraces) - - /* Tempo Tag Handlers */ - - const handlerTempoLabel = require('./lib/handlers/tempo_tags').bind(this) - readerMode && fastify.get('/api/search/tags', handlerTempoLabel) - readerMode && fastify.get('/tempo/api/search/tags', handlerTempoLabel) - - const handlerTempoLabelV2 = require('./lib/handlers/tempo_v2_tags').bind(this) - readerMode && fastify.get('/api/v2/search/tags', handlerTempoLabelV2) - readerMode && fastify.get('/tempo/api/v2/search/tags', handlerTempoLabelV2) - - /* Tempo Tag Value Handler */ - const handlerTempoLabelValues = require('./lib/handlers/tempo_values').bind(this) - readerMode && fastify.get('/api/search/tag/:name/values', handlerTempoLabelValues) - readerMode && fastify.get('/tempo/api/search/tag/:name/values', handlerTempoLabelValues) - - const handlerTempoLabelV2Values = require('./lib/handlers/tempo_v2_values').bind(this) - readerMode && fastify.get('/api/v2/search/tag/:name/values', handlerTempoLabelV2Values) - readerMode && fastify.get('/tempo/api/v2/search/tag/:name/values', handlerTempoLabelV2Values) - - /* Tempo Traces Query Handler */ - const handlerTempoSearch = require('./lib/handlers/tempo_search.js').bind(this) - readerMode && fastify.get('/api/search', handlerTempoSearch) - readerMode && fastify.get('/tempo/api/search', handlerTempoSearch) - - /* Tempo Echo Handler */ - const handlerTempoEcho = require('./lib/handlers/echo.js').bind(this) - fastify.get('/api/echo', handlerTempoEcho) - fastify.get('/tempo/api/echo', handlerTempoEcho) - - /* Telegraf HTTP Bulk handler */ - const handlerTelegraf = require('./lib/handlers/telegraf.js').bind(this) - writerMode && fastify.post('/telegraf', handlerTelegraf, { - '*': jsonParser - }) - - /* Datadog Log Push Handler */ - const handlerDatadogLogPush = require('./lib/handlers/datadog_log_push.js').bind(this) - writerMode && fastify.post('/api/v2/logs', handlerDatadogLogPush, { - 'application/json': jsonParser, - '*': rawStringParser - }) - - /* Datadog Series Push Handler */ - const handlerDatadogSeriesPush = require('./lib/handlers/datadog_series_push.js').bind(this) - writerMode && fastify.post('/api/v2/series', handlerDatadogSeriesPush, { - 'application/json': jsonParser, - '*': rawStringParser - }) - - /* Query Handler */ - const handlerQueryRange = require('./lib/handlers/query_range.js').bind(this) - readerMode && fastify.get('/loki/api/v1/query_range', handlerQueryRange) - - /* Label Handlers */ - /* Label Value Handler via query (test) */ - const handlerQuery = require('./lib/handlers/query.js').bind(this) - readerMode && fastify.get('/loki/api/v1/query', handlerQuery) - - /* Label Handlers */ - const handlerLabel = require('./lib/handlers/label.js').bind(this) - readerMode && fastify.get('/loki/api/v1/label', handlerLabel) - readerMode && fastify.get('/loki/api/v1/labels', handlerLabel) - - /* Label Value Handler */ - const handlerLabelValues = require('./lib/handlers/label_values.js').bind(this) - readerMode && fastify.get('/loki/api/v1/label/:name/values', handlerLabelValues) - - /* Series Handler - experimental support for both Loki and Prometheus */ - const handlerSeries = require('./lib/handlers/series.js').bind(this) - readerMode && fastify.get('/loki/api/v1/series', handlerSeries) - const handlerPromSeries = require('./lib/handlers/prom_series.js').bind(this) - readerMode && fastify.get('/api/v1/series', handlerPromSeries) - readerMode && fastify.post('/api/v1/series', handlerPromSeries, { - 'application/x-www-form-urlencoded': wwwFormParser - }) - - readerMode && fastify.register(async (fastify) => { - fastify.get('/loki/api/v1/tail', { websocket: true }, require('./lib/handlers/tail').bind(this)) - }) - - /* ALERT MANAGER Handlers */ - readerMode && fastify.get('/api/prom/rules', require('./lib/handlers/alerts/get_rules').bind(this)) - readerMode && fastify.get('/api/prom/rules/:ns/:group', require('./lib/handlers/alerts/get_group').bind(this)) - readerMode && fastify.post('/api/prom/rules/:ns', require('./lib/handlers/alerts/post_group').bind(this), { - '*': yamlParser - }) - readerMode && fastify.delete('/api/prom/rules/:ns/:group', require('./lib/handlers/alerts/del_group').bind(this)) - readerMode && fastify.delete('/api/prom/rules/:ns', require('./lib/handlers/alerts/del_ns').bind(this)) - readerMode && fastify.get('/prometheus/api/v1/rules', require('./lib/handlers/alerts/prom_get_rules').bind(this)) - - /* PROMETHEUS REMOTE WRITE Handlers */ - const promWriteHandler = require('./lib/handlers/prom_push.js').bind(this) - const remoteWritePaths = [ - '/api/v1/prom/remote/write', - '/api/prom/remote/write', - '/prom/remote/write', - '/api/v1/write' - ] - for (const path of remoteWritePaths) { - writerMode && fastify.post(path, promWriteHandler, { - 'application/x-protobuf': prometheusPushProtoParser, - 'application/json': jsonParser, - '*': combinedParser(prometheusPushProtoParser, jsonParser) - }) - writerMode && fastify.get(path, handlerTempoEcho) - } - - /* PROMQETHEUS API EMULATION */ - const handlerPromQueryRange = require('./lib/handlers/prom_query_range.js').bind(this) - readerMode && fastify.post('/api/v1/query_range', handlerPromQueryRange, { - 'application/x-www-form-urlencoded': wwwFormParser - }) - readerMode && fastify.get('/api/v1/query_range', handlerPromQueryRange) - const handlerPromQuery = require('./lib/handlers/prom_query.js').bind(this) - readerMode && fastify.post('/api/v1/query', handlerPromQuery, { - 'application/x-www-form-urlencoded': wwwFormParser - }) - readerMode && fastify.get('/api/v1/query', handlerPromQuery) - const handlerPromLabel = require('./lib/handlers/promlabel.js').bind(this) - const handlerPromLabelValues = require('./lib/handlers/promlabel_values.js').bind(this) - readerMode && fastify.get('/api/v1/labels', handlerPromLabel) // piggyback on qryn labels - readerMode && fastify.get('/api/v1/label/:name/values', handlerPromLabelValues) // piggyback on qryn values - readerMode && fastify.post('/api/v1/labels', handlerPromLabel, { - '*': rawStringParser - }) // piggyback on qryn labels - readerMode && fastify.post('/api/v1/label/:name/values', handlerPromLabelValues, { - '*': rawStringParser - }) // piggyback on qryn values - const handlerPromDefault = require('./lib/handlers/prom_default.js') - readerMode && fastify.get('/api/v1/metadata', handlerPromDefault.misc.bind(this)) // default handler TBD - readerMode && fastify.get('/api/v1/rules', handlerPromDefault.rules.bind(this)) // default handler TBD - readerMode && fastify.get('/api/v1/query_exemplars', handlerPromDefault.misc.bind(this)) // default handler TBD - readerMode && fastify.post('/api/v1/query_exemplars', handlerPromDefault.misc.bind(this), { - 'application/x-www-form-urlencoded': wwwFormParser - }) // default handler TBD - readerMode && fastify.get('/api/v1/format_query', handlerPromDefault.misc.bind(this)) // default handler TBD - readerMode && fastify.post('/api/v1/format_query', handlerPromDefault.misc.bind(this), { - 'application/x-www-form-urlencoded': wwwFormParser - }) // default handler TBD - fastify.get('/api/v1/status/buildinfo', handlerPromDefault.buildinfo.bind(this)) // default handler TBD - - /* NewRelic Log Handler */ - const handlerNewrelicLogPush = require('./lib/handlers/newrelic_log_push.js').bind(this) - writerMode && fastify.post('/log/v1', handlerNewrelicLogPush, { - 'text/plain': jsonParser, - '*': jsonParser - }) - - /* INFLUX WRITE Handlers */ - const handlerInfluxWrite = require('./lib/handlers/influx_write.js').bind(this) - writerMode && fastify.post('/write', handlerInfluxWrite, { - '*': rawStringParser - }) - writerMode && fastify.post('/influx/api/v2/write', handlerInfluxWrite, { - '*': rawStringParser - }) - /* INFLUX HEALTH Handlers */ - const handlerInfluxHealth = require('./lib/handlers/influx_health.js').bind(this) - fastify.get('/health', handlerInfluxHealth) - fastify.get('/influx/health', handlerInfluxHealth) - - const handlerOTLPPush = require('./lib/handlers/otlp_push').bind(this) - writerMode && fastify.post('/v1/traces', handlerOTLPPush, { - '*': otlpPushProtoParser - }) - - fastify = parsers.init(fastify) - - /* QRYN-VIEW Optional Handler */ - if (fs.existsSync(path.join(__dirname, 'view/index.html'))) { - fastify.register(require('@fastify/static'), { - root: path.join(__dirname, 'view'), - prefix: '/' - }) - const idx = fs.readFileSync(path.join(__dirname, 'view/index.html'), 'utf8') - for (const fakePath of ['/plugins', '/users', '/datasources', '/datasources/:ds']) { - fastify.get(fakePath, - (req, reply) => - reply.code(200).header('Content-Type', 'text/html').send(idx)) - } - } - - readerMode && require('./pyroscope/pyroscope').init(fastify) - - const handleOTLPLogs = require('./lib/handlers/otlp_log_push').bind(this) - writerMode && fastify.post('/v1/logs', handleOTLPLogs, { - '*': otlpLogsDataParser - }) - - // Run API Service - fastify.listen( - { - port: process.env.PORT || 3100, - host: process.env.HOST || '0.0.0.0' - }, - (err, address) => { - if (err) throw err - logger.info('Qryn API up') - fastify.log.info(`Qryn API listening on ${address}`) - } - ) -})() - -module.exports.stop = () => { - shaper.stop() - profiler && clearInterval(profiler) - fastify.close() - DATABASE.stop() - require('./parser/transpiler').stop() - stop() -} diff --git a/qryn_node_wrapper.js b/qryn_node_wrapper.js deleted file mode 100644 index f7fe3b15..00000000 --- a/qryn_node_wrapper.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports.init = () => { - require('./qryn_node') -} -module.exports.bun = () => { - try { - return Bun - } catch (e) { - return false - } -} diff --git a/reader/config/config.go b/reader/config/config.go new file mode 100644 index 00000000..7100bace --- /dev/null +++ b/reader/config/config.go @@ -0,0 +1,5 @@ +package config + +import clconfig "github.com/metrico/cloki-config" + +var Cloki *clconfig.ClokiConfig diff --git a/reader/controller/controllerController.go b/reader/controller/controllerController.go new file mode 100644 index 00000000..eb60149d --- /dev/null +++ b/reader/controller/controllerController.go @@ -0,0 +1,4 @@ +package controllerv1 + +type Controller struct { +} diff --git a/reader/controller/miscController.go b/reader/controller/miscController.go new file mode 100644 index 00000000..a2cac219 --- /dev/null +++ b/reader/controller/miscController.go @@ -0,0 +1,70 @@ +package controllerv1 + +import ( + jsoniter "github.com/json-iterator/go" + "github.com/metrico/qryn/reader/utils/logger" + watchdog "github.com/metrico/qryn/reader/watchdog" + "net/http" +) + +type MiscController struct { + Version string +} + +func (uc *MiscController) Ready(w http.ResponseWriter, r *http.Request) { + err := watchdog.Check() + if err != nil { + w.WriteHeader(500) + logger.Error(err.Error()) + w.Write([]byte("Internal Server Error")) + return + } + w.WriteHeader(200) + w.Write([]byte("OK")) +} + +func (uc *MiscController) Config(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte("Not supported")) +} + +func (uc *MiscController) Rules(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"data": {"groups": []},"status": "success"}`)) +} + +func (uc *MiscController) Metadata(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"status": "success","data": {}}`)) +} + +func (uc *MiscController) Buildinfo(w http.ResponseWriter, r *http.Request) { + //w.Header().Set("Content-Type", "application/json") + //w.WriteHeader(http.StatusOK) + //w.Write([]byte(fmt.Sprintf(`{"status": "success","data": {"version": "%s"}}`, uc.Version))) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + json := jsoniter.ConfigFastest + stream := json.BorrowStream(nil) + defer json.ReturnStream(stream) + stream.WriteObjectStart() + stream.WriteObjectField("status") + stream.WriteString("success") + stream.WriteMore() + + stream.WriteObjectField("data") + stream.WriteObjectStart() + + stream.WriteObjectField("version") + stream.WriteString(uc.Version) + + stream.WriteObjectEnd() + stream.WriteObjectEnd() + + w.Write(stream.Buffer()) + +} diff --git a/reader/controller/profController.go b/reader/controller/profController.go new file mode 100644 index 00000000..a1287ea8 --- /dev/null +++ b/reader/controller/profController.go @@ -0,0 +1,316 @@ +package controllerv1 + +import "html" + +import ( + "encoding/json" + "fmt" + "github.com/metrico/qryn/reader/prof" + v1 "github.com/metrico/qryn/reader/prof/types/v1" + "github.com/metrico/qryn/reader/service" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + "io" + "net/http" + "strconv" + "time" +) + +type ProfController struct { + Controller + ProfService *service.ProfService +} + +func (pc *ProfController) NotImplemented(w http.ResponseWriter, r *http.Request) { + // TODO: Implement this + w.WriteHeader(http.StatusNotImplemented) +} + +func (pc *ProfController) ProfileTypes(w http.ResponseWriter, r *http.Request) { + var req prof.ProfileTypesRequest + err := defaultParser(r, &req) + if err != nil { + defaultError(w, 400, err.Error()) + return + } + res, err := pc.ProfService.ProfileTypes(r.Context(), + time.Unix(0, req.Start*1000000), + time.Unix(0, req.End*1000000)) + if err != nil { + defaultError(w, 500, err.Error()) + return + } + if len(res) == 0 { + res = append(res, &v1.ProfileType{ + ID: "", + Name: "", + SampleType: "", + SampleUnit: "", + PeriodType: "", + PeriodUnit: "", + }) + } + pc.writeResponse(w, r, &prof.ProfileTypesResponse{ + ProfileTypes: res, + }) +} + +func (pc *ProfController) LabelNames(w http.ResponseWriter, r *http.Request) { + var req v1.LabelNamesRequest + err := defaultParser(r, &req) + if err != nil { + defaultError(w, 400, err.Error()) + return + } + res, err := pc.ProfService.LabelNames( + r.Context(), + req.Matchers, + time.UnixMilli(req.Start), + time.UnixMilli(req.End)) + if err != nil { + defaultError(w, 500, err.Error()) + return + } + if len(res.Names) == 0 { + res.Names = append(res.Names, "") + } + pc.writeResponse(w, r, res) +} + +func (pc *ProfController) LabelValues(w http.ResponseWriter, r *http.Request) { + var req v1.LabelValuesRequest + err := defaultParser(r, &req) + if err != nil { + defaultError(w, 400, err.Error()) + return + } + res, err := pc.ProfService.LabelValues( + r.Context(), + req.Matchers, + req.Name, + time.UnixMilli(req.Start), + time.UnixMilli(req.End)) + if err != nil { + defaultError(w, 500, err.Error()) + return + } + + pc.writeResponse(w, r, res) +} + +func (pc *ProfController) SelectMergeStackTraces(w http.ResponseWriter, r *http.Request) { + var req prof.SelectMergeStacktracesRequest + err := defaultParser(r, &req) + if err != nil { + defaultError(w, 400, err.Error()) + return + } + res, err := pc.ProfService.MergeStackTraces( + r.Context(), + req.LabelSelector, + req.ProfileTypeID, + time.UnixMilli(req.Start), + time.UnixMilli(req.End)) + if err != nil { + defaultError(w, 500, err.Error()) + return + } + pc.writeResponse(w, r, res) +} + +func (pc *ProfController) SelectSeries(w http.ResponseWriter, r *http.Request) { + var req prof.SelectSeriesRequest + err := defaultParser(r, &req) + if err != nil { + defaultError(w, 400, err.Error()) + return + } + agg := v1.TimeSeriesAggregationType_TIME_SERIES_AGGREGATION_TYPE_SUM + if req.Aggregation != nil { + agg = *req.Aggregation + } + res, err := pc.ProfService.SelectSeries( + r.Context(), + req.LabelSelector, + req.ProfileTypeID, + req.GroupBy, + agg, + int64(req.Step), + time.UnixMilli(req.Start), + time.UnixMilli(req.End)) + if err != nil { + defaultError(w, 500, err.Error()) + return + } + pc.writeResponse(w, r, res) +} + +func (pc *ProfController) MergeProfiles(w http.ResponseWriter, r *http.Request) { + var req prof.SelectMergeProfileRequest + err := defaultParser(r, &req) + if err != nil { + defaultError(w, 400, err.Error()) + return + } + res, err := pc.ProfService.MergeProfiles( + r.Context(), + req.LabelSelector, + req.ProfileTypeID, + time.UnixMilli(req.Start), + time.UnixMilli(req.End)) + if err != nil { + defaultError(w, 500, err.Error()) + return + } + pc.writeResponse(w, r, res) +} + +func (pc *ProfController) Series(w http.ResponseWriter, r *http.Request) { + var req prof.SeriesRequest + err := defaultParser(r, &req) + if err != nil { + defaultError(w, 400, err.Error()) + return + } + res, err := pc.ProfService.TimeSeries( + r.Context(), + req.Matchers, + req.LabelNames, + time.UnixMilli(req.Start), + time.UnixMilli(req.End)) + if err != nil { + defaultError(w, 500, err.Error()) + return + } + pc.writeResponse(w, r, res) +} + +func (pc *ProfController) ProfileStats(w http.ResponseWriter, r *http.Request) { + res, err := pc.ProfService.ProfileStats(r.Context()) + if err != nil { + defaultError(w, 500, err.Error()) + return + } + + pc.writeResponse(w, r, res) +} + +func (pc *ProfController) Settings(w http.ResponseWriter, r *http.Request) { + res, err := pc.ProfService.Settings(r.Context()) + if err != nil { + defaultError(w, 500, err.Error()) + return + } + pc.writeResponse(w, r, res) +} + +func (pc *ProfController) RenderDiff(w http.ResponseWriter, r *http.Request) { + for _, param := range []string{"leftQuery", "leftFrom", "leftUntil", "rightQuery", "rightFrom", "rightUntil"} { + if len(r.URL.Query()[param]) == 0 || r.URL.Query()[param][0] == "" { + defaultError(w, 400, fmt.Sprintf("Missing required parameter: %s", param)) + return + } + } + + leftQuery := r.URL.Query()["leftQuery"][0] + rightQuery := r.URL.Query()["rightQuery"][0] + var ( + leftFrom, leftTo, rightFrom, rightTo time.Time + ) + for _, v := range [][2]any{ + {"leftFrom", &leftFrom}, {"leftUntil", &leftTo}, {"rightFrom", &rightFrom}, {"rightUntil", &rightTo}} { + strVal := r.URL.Query()[v[0].(string)][0] + iVal, err := strconv.ParseInt(strVal, 10, 64) + if err != nil { + defaultError(w, 400, fmt.Sprintf("Invalid value for %s: %s", html.EscapeString(v[0].(string)), html.EscapeString(strVal))) + return + } + *(v[1].(*time.Time)) = time.Unix(iVal/1000, 0) + } + diff, err := pc.ProfService.RenderDiff( + r.Context(), + leftQuery, + rightQuery, + leftFrom, + rightFrom, + leftTo, + rightTo) + if err != nil { + defaultError(w, 500, err.Error()) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(diff.FlamebearerProfileV1) +} + +func (pc *ProfController) AnalyzeQuery(w http.ResponseWriter, r *http.Request) { + var req prof.AnalyzeQueryRequest + err := defaultParser(r, &req) + if err != nil { + defaultError(w, 400, err.Error()) + return + } + res, err := pc.ProfService.AnalyzeQuery( + r.Context(), + req.Query, + time.UnixMilli(req.Start), + time.UnixMilli(req.End), + ) + if err != nil { + defaultError(w, 500, err.Error()) + return + } + pc.writeResponse(w, r, res) +} + +func (pc *ProfController) writeResponse(w http.ResponseWriter, r *http.Request, data proto.Message) { + contentType := r.Header.Get("Content-Type") + bData, err := defaultMarshaller(r, data) + if err != nil { + defaultError(w, 500, err.Error()) + return + } + w.Header().Set("Content-Type", contentType) + w.Write(bData) +} + +func defaultParser(r *http.Request, res proto.Message) error { + contentType := r.Header.Get("Content-Type") + body, err := io.ReadAll(r.Body) + if err != nil { + return err + } + if contentType == "application/json" { + err = json.Unmarshal(body, res) + } else { + err = proto.Unmarshal(body, res) + } + if err != nil { + return err + } + return nil +} + +func defaultMarshaller[T proto.Message](r *http.Request, t T) ([]byte, error) { + contentType := r.Header.Get("Content-Type") + if contentType == "application/json" { + return protojson.MarshalOptions{ + UseEnumNumbers: false, + EmitUnpopulated: false, + UseProtoNames: false, + }.Marshal(t) + } + + data, err := proto.Marshal(t) + if err != nil { + return nil, err + } + return data, nil +} + +func defaultError(w http.ResponseWriter, code int, message string) { + w.WriteHeader(code) + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(strconv.Quote(message))) +} diff --git a/reader/controller/promQueryInstantController.go b/reader/controller/promQueryInstantController.go new file mode 100644 index 00000000..9d4e2279 --- /dev/null +++ b/reader/controller/promQueryInstantController.go @@ -0,0 +1,78 @@ +package controllerv1 + +import ( + "fmt" + "github.com/gorilla/schema" + "net/http" + "time" +) + +type queryInstantProps struct { + Raw struct { + Time string `form:"time"` + Query string `form:"query"` + } + Time time.Time + Query string +} + +func (q *PromQueryRangeController) QueryInstant(w http.ResponseWriter, r *http.Request) { + defer tamePanic(w, r) + ctx, err := RunPreRequestPlugins(r) + if err != nil { + PromError(500, err.Error(), w) + return + } + req, err := parseQueryInstantProps(r) + if err != nil { + PromError(400, err.Error(), w) + return + } + promQuery, err := q.Api.QueryEngine.NewInstantQuery(q.Storage.SetOidAndDB(ctx), nil, req.Query, req.Time) + if err != nil { + PromError(500, err.Error(), w) + return + } + res := promQuery.Exec(ctx) + if res.Err != nil { + PromError(500, res.Err.Error(), w) + return + } + err = writeResponse(res, w) + if err != nil { + PromError(500, err.Error(), w) + return + } +} + +func parseQueryInstantProps(r *http.Request) (queryInstantProps, error) { + res := queryInstantProps{} + var err error + if r.Method == "POST" && r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" { + err = r.ParseForm() + if err != nil { + return res, err + } + + dec := schema.NewDecoder() + err = dec.Decode(&res.Raw, r.Form) + if err != nil { + return res, err + } + } + if res.Raw.Query == "" { + res.Raw.Query = r.URL.Query().Get("query") + } + if res.Raw.Time == "" { + res.Raw.Time = r.URL.Query().Get("time") + } + res.Time, err = ParseTimeSecOrRFC(res.Raw.Time, time.Now()) + if err != nil { + return res, err + } + if res.Raw.Query == "" { + return res, fmt.Errorf("query is undefined") + } + res.Query = res.Raw.Query + return res, err +} diff --git a/reader/controller/promQueryLabelsController.go b/reader/controller/promQueryLabelsController.go new file mode 100644 index 00000000..c3355413 --- /dev/null +++ b/reader/controller/promQueryLabelsController.go @@ -0,0 +1,177 @@ +package controllerv1 + +import ( + "github.com/gorilla/mux" + "github.com/gorilla/schema" + "github.com/metrico/qryn/reader/service" + "net/http" + "strconv" + "time" +) + +type PromQueryLabelsController struct { + Controller + QueryLabelsService *service.QueryLabelsService +} + +type promLabelsParams struct { + start time.Time + end time.Time +} + +type rawPromLabelsParams struct { + Start string `form:"start"` + End string `form:"end"` +} + +type promSeriesParams struct { + Match []string `form:"match[]"` +} + +func (p *PromQueryLabelsController) PromLabels(w http.ResponseWriter, r *http.Request) { + defer tamePanic(w, r) + internalCtx, err := RunPreRequestPlugins(r) + if err != nil { + PromError(500, err.Error(), w) + return + } + params, err := getLabelsParams(r) + if err != nil { + PromError(400, err.Error(), w) + return + } + res, err := p.QueryLabelsService.Labels(internalCtx, params.start.UnixMilli(), params.end.UnixMilli(), 2) + if err != nil { + PromError(500, err.Error(), w) + return + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + for str := range res { + w.Write([]byte(str)) + } +} + +func (p *PromQueryLabelsController) LabelValues(w http.ResponseWriter, r *http.Request) { + defer tamePanic(w, r) + internalCtx, err := RunPreRequestPlugins(r) + if err != nil { + PromError(500, err.Error(), w) + return + } + params, err := ParseLogSeriesParamsV2(r, time.Second) + name := mux.Vars(r)["name"] + if name == "" { + PromError(400, "label name is required", w) + return + } + res, err := p.QueryLabelsService.PromValues(internalCtx, name, params.Match, + params.ValuesParams.Start.UnixMilli(), params.ValuesParams.End.UnixMilli(), 2) + if err != nil { + PromError(500, err.Error(), w) + return + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + for str := range res { + w.Write([]byte(str)) + } +} + +func (p *PromQueryLabelsController) Metadata(w http.ResponseWriter, r *http.Request) { + defer tamePanic(w, r) + _, err := RunPreRequestPlugins(r) + if err != nil { + PromError(500, err.Error(), w) + return + } + w.WriteHeader(200) + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{"status": "success", "data": {}}`)) +} + +func (p *PromQueryLabelsController) Series(w http.ResponseWriter, r *http.Request) { + defer tamePanic(w, r) + internalCtx, err := RunPreRequestPlugins(r) + if err != nil { + PromError(500, err.Error(), w) + return + } + params, err := getLabelsParams(r) + if err != nil { + PromError(400, err.Error(), w) + return + } + seriesParams, err := getPromSeriesParamsV2(r) + if err != nil { + PromError(400, err.Error(), w) + return + } + + res, err := p.QueryLabelsService.Series(internalCtx, seriesParams.Match, params.start.UnixMilli(), + params.end.UnixMilli(), 2) + if err != nil { + PromError(500, err.Error(), w) + return + } + w.WriteHeader(200) + w.Header().Set("Content-Type", "application/json") + for str := range res { + w.Write([]byte(str)) + } +} + +func getPromSeriesParamsV2(r *http.Request) (promSeriesParams, error) { + res := promSeriesParams{} + if r.Method == "POST" && r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" { + err := r.ParseForm() + if err != nil { + return res, err + } + for key, value := range r.Form { + if key == "match[]" { + res.Match = append(res.Match, value...) + } + } + } + for _, v := range r.URL.Query()["match[]"] { + res.Match = append(res.Match, v) + } + return res, nil +} + +func parserTimeString(strTime string, def time.Time) time.Time { + tTime, err := time.Parse(time.RFC3339, strTime) + if err == nil { + return tTime + } + iTime, err := strconv.ParseInt(strTime, 10, 63) + if err == nil { + return time.Unix(iTime, 0) + } + return def +} + +func getLabelsParams(r *http.Request) (*promLabelsParams, error) { + if r.Method == "POST" && r.Header.Get("content-type") == "application/x-www-form-urlencoded" { + rawParams := rawPromLabelsParams{} + dec := schema.NewDecoder() + err := r.ParseForm() + if err != nil { + return nil, err + } + err = dec.Decode(&rawParams, r.Form) + if err != nil { + return nil, err + } + return &promLabelsParams{ + start: parserTimeString(rawParams.Start, time.Now().Add(time.Hour*-6)), + end: parserTimeString(rawParams.End, time.Now()), + }, nil + } + + return &promLabelsParams{ + start: parserTimeString(r.URL.Query().Get("start"), time.Now().Add(time.Hour*-6)), + end: parserTimeString(r.URL.Query().Get("end"), time.Now().Add(time.Hour*-6)), + }, nil +} diff --git a/reader/controller/promQueryRangeController.go b/reader/controller/promQueryRangeController.go new file mode 100644 index 00000000..8ff458ce --- /dev/null +++ b/reader/controller/promQueryRangeController.go @@ -0,0 +1,412 @@ +package controllerv1 + +import ( + "fmt" + "github.com/gofiber/fiber/v2" + "github.com/gorilla/schema" + jsoniter "github.com/json-iterator/go" + "github.com/metrico/qryn/reader/service" + "github.com/metrico/qryn/reader/utils/logger" + "github.com/pkg/errors" + "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/promql" + api_v1 "github.com/prometheus/prometheus/web/api/v1" + "math" + "net/http" + "strconv" + "time" +) + +type PromQueryRangeController struct { + Controller + Api *api_v1.API + Storage *service.CLokiQueriable + Stats bool +} +type QueryRangeProps struct { + Start time.Time + End time.Time + Query string + Step time.Duration + Raw struct { + Start string `form:"start"` + End string `form:"end"` + Query string `form:"query"` + Step string `form:"step"` + } +} + +func (q *PromQueryRangeController) QueryRange(w http.ResponseWriter, r *http.Request) { + defer tamePanic(w, r) + internalCtx, err := RunPreRequestPlugins(r) + if err != nil { + PromError(500, err.Error(), w) + return + } + req, err := parseQueryRangePropsV2(r) + if err != nil { + PromError(400, err.Error(), w) + return + } + req.Start = time.Unix(req.Start.Unix()/15*15, 0) + req.End = time.Unix(int64(math.Ceil(float64(req.End.Unix())/15)*15), 0) + if req.Step <= 0 { + PromError(400, + "zero or negative query resolution step widths are not accepted. Try a positive integer", + w) + return + } + // For safety, limit the number of returned points per timeseries. + // This is sufficient for 60s resolution for a week or 1h resolution for a year. + if req.End.Sub(req.Start)/req.Step > 11000 { + PromError( + 500, + "exceeded maximum resolution of 11,000 points per timeseries. Try decreasing the query resolution (?step=XX)", + w) + return + } + rangeQuery, err := q.Api.QueryEngine.NewRangeQuery(q.Storage.SetOidAndDB(internalCtx), nil, + req.Query, req.Start, req.End, req.Step) + if err != nil { + logger.Error("[PQRC001] " + err.Error()) + PromError(500, err.Error(), w) + return + } + res := rangeQuery.Exec(internalCtx) + if res.Err != nil { + logger.Error("[PQRC002] " + res.Err.Error()) + PromError(500, res.Err.Error(), w) + return + } + err = writeResponse(res, w) + if err != nil { + logger.Error("[PQRC003] " + err.Error()) + PromError(500, err.Error(), w) + return + } +} + +func parseQueryRangePropsV2(r *http.Request) (QueryRangeProps, error) { + res := QueryRangeProps{} + var err error + if r.Method == "POST" && r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" { + err = r.ParseForm() + if err != nil { + return res, err + } + dec := schema.NewDecoder() + err = dec.Decode(&res.Raw, r.Form) + } + if res.Raw.Start == "" { + res.Raw.Start = r.URL.Query().Get("start") + } + if res.Raw.End == "" { + res.Raw.End = r.URL.Query().Get("end") + } + if res.Raw.Query == "" { + res.Raw.Query = r.URL.Query().Get("query") + } + if res.Raw.Step == "" { + res.Raw.Step = r.URL.Query().Get("step") + } + res.Start, err = ParseTimeSecOrRFC(res.Raw.Start, time.Now().Add(time.Hour*-6)) + if err != nil { + return res, err + } + res.End, err = ParseTimeSecOrRFC(res.Raw.End, time.Now()) + if err != nil { + return res, err + } + res.Query = res.Raw.Query + if res.Query == "" { + return res, fmt.Errorf("query is undefined") + } + res.Step, err = parseDuration(res.Raw.Step) + return res, err +} + +func parseQueryRangeProps(ctx *fiber.Ctx) (QueryRangeProps, error) { + res := QueryRangeProps{} + var err error + if ctx.Method() == "POST" && ctx.Get(fiber.HeaderContentType) == fiber.MIMEApplicationForm { + err = ctx.BodyParser(&res.Raw) + if err != nil { + return res, err + } + } + if res.Raw.Start == "" { + res.Raw.Start = ctx.Query("start") + } + if res.Raw.End == "" { + res.Raw.End = ctx.Query("end") + } + if res.Raw.Query == "" { + res.Raw.Query = ctx.Query("query") + } + if res.Raw.Step == "" { + res.Raw.Step = ctx.Query("step") + } + res.Start, err = ParseTimeSecOrRFC(res.Raw.Start, time.Now().Add(time.Hour*-6)) + if err != nil { + return res, err + } + res.End, err = ParseTimeSecOrRFC(res.Raw.End, time.Now()) + if err != nil { + return res, err + } + res.Query = res.Raw.Query + if res.Query == "" { + return res, fmt.Errorf("query is undefined") + } + res.Step, err = parseDuration(res.Raw.Step) + return res, err +} + +//func PromError(code int, msg string, w http.ResponseWriter) { +// w.WriteHeader(code) +// w.Header().Set("Content-Type", "application/json") +// w.Write([]byte(fmt.Sprintf(`{"status": "error", "errorType":"error", "error": %s}`, +// strconv.Quote(msg)))) +//} + +func PromError(code int, msg string, w http.ResponseWriter) { + w.WriteHeader(code) + w.Header().Set("Content-Type", "application/json") + + json := jsoniter.ConfigFastest + stream := json.BorrowStream(nil) + defer json.ReturnStream(stream) + + stream.WriteObjectStart() + stream.WriteObjectField("status") + stream.WriteString("error") + stream.WriteMore() + stream.WriteObjectField("errorType") + stream.WriteString("error") + stream.WriteMore() + stream.WriteObjectField("error") + stream.WriteString(msg) + stream.WriteObjectEnd() + + w.Write(stream.Buffer()) +} + +//func writeResponse(res *promql.Result, w http.ResponseWriter) error { +// var err error +// w.Header().Set("Content-Type", "application/json") +// _, err = w.Write([]byte(fmt.Sprintf(`{"status" : "success", "data" : {"resultType" : "%s", "result" : [`, +// res.Value.Type()))) +// if err != nil { +// return err +// } +// switch res.Value.(type) { +// case promql.Matrix: +// err = writeMatrix(res, w) +// break +// case promql.Vector: +// err = writeVector(res, w) +// break +// case promql.Scalar: +// err = writeScalar(res, w) +// } +// if err != nil { +// return err +// } +// w.Write([]byte("]}}")) +// return nil +//} + +func writeResponse(res *promql.Result, w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + + json := jsoniter.ConfigFastest + stream := json.BorrowStream(nil) + defer json.ReturnStream(stream) + + stream.WriteObjectStart() + stream.WriteObjectField("status") + stream.WriteString("success") + stream.WriteMore() + stream.WriteObjectField("data") + stream.WriteObjectStart() + stream.WriteObjectField("resultType") + stream.WriteString(string(res.Value.Type())) + stream.WriteMore() + stream.WriteObjectField("result") + stream.WriteArrayStart() + + _, err := w.Write(stream.Buffer()) + if err != nil { + return err + } + stream.Reset(nil) + + switch res.Value.(type) { + case promql.Matrix: + err = writeMatrix(res, w) + case promql.Vector: + err = writeVector(res, w) + case promql.Scalar: + err = writeScalar(res, w) + } + + if err != nil { + return err + } + + w.Write([]byte("]}}")) + return nil +} + +func writeScalar(res *promql.Result, w http.ResponseWriter) error { + val := res.Value.(promql.Scalar) + w.Write([]byte(fmt.Sprintf(`%f, "%f"`, float64(val.T)/1000, val.V))) + return nil +} + +// func writeMatrix(res *promql.Result, w http.ResponseWriter) error { +// val := res.Value.(promql.Matrix) +// for i, s := range val { +// if i > 0 { +// w.Write([]byte(",")) +// } +// w.Write([]byte(`{"metric": {`)) +// for j, v := range s.Metric { +// if j > 0 { +// w.Write([]byte(",")) +// } +// w.Write([]byte(fmt.Sprintf("%s:%s", strconv.Quote(v.Name), strconv.Quote(v.Value)))) +// } +// w.Write([]byte(`},"values": [`)) +// for j, v := range s.Points { +// if j > 0 { +// w.Write([]byte(",")) +// } +// w.Write([]byte(fmt.Sprintf(`[%f,"%f"]`, float64(v.T)/1000, v.V))) +// } +// w.Write([]byte("]}")) +// } +// return nil +// } +func writeMatrix(res *promql.Result, w http.ResponseWriter) error { + val := res.Value.(promql.Matrix) + + json := jsoniter.ConfigFastest + + for i, s := range val { + if i > 0 { + w.Write([]byte(",")) + } + + stream := json.BorrowStream(nil) + + stream.WriteObjectStart() + stream.WriteObjectField("metric") + stream.WriteObjectStart() + + for j, v := range s.Metric { + if j > 0 { + stream.WriteMore() + } + stream.WriteObjectField(v.Name) + stream.WriteString(v.Value) + } + + stream.WriteObjectEnd() + stream.WriteMore() + stream.WriteObjectField("values") + stream.WriteArrayStart() + + for j, v := range s.Points { + if j > 0 { + stream.WriteMore() + } + stream.WriteArrayStart() + stream.WriteFloat64(float64(v.T) / 1000) + stream.WriteMore() + stream.WriteString(strconv.FormatFloat(v.V, 'f', -1, 64)) + stream.WriteArrayEnd() + } + + stream.WriteArrayEnd() + stream.WriteObjectEnd() + + w.Write(stream.Buffer()) + json.ReturnStream(stream) + } + + return nil +} + +//func writeVector(res *promql.Result, w http.ResponseWriter) error { +// val := res.Value.(promql.Vector) +// for i, s := range val { +// if i > 0 { +// w.Write([]byte(",")) +// } +// w.Write([]byte(`{"metric":{`)) +// for j, lbl := range s.Metric { +// if j > 0 { +// w.Write([]byte(",")) +// } +// w.Write([]byte(fmt.Sprintf("%s:%s", strconv.Quote(lbl.Name), strconv.Quote(lbl.Value)))) +// } +// w.Write([]byte(fmt.Sprintf(`},"value":[%f,"%f"]}`, float64(s.T/1000), s.V))) +// } +// return nil +//} + +func writeVector(res *promql.Result, w http.ResponseWriter) error { + val := res.Value.(promql.Vector) + + json := jsoniter.ConfigFastest + + for i, s := range val { + if i > 0 { + w.Write([]byte(",")) + } + + stream := json.BorrowStream(nil) + + stream.WriteObjectStart() + stream.WriteObjectField("metric") + stream.WriteObjectStart() + + for j, lbl := range s.Metric { + if j > 0 { + stream.WriteMore() + } + stream.WriteObjectField(lbl.Name) + stream.WriteString(lbl.Value) + } + + stream.WriteObjectEnd() + stream.WriteMore() + stream.WriteObjectField("value") + stream.WriteArrayStart() + stream.WriteFloat64(float64(s.T) / 1000) + stream.WriteMore() + stream.WriteString(strconv.FormatFloat(s.V, 'f', -1, 64)) + stream.WriteArrayEnd() + stream.WriteObjectEnd() + + w.Write(stream.Buffer()) + json.ReturnStream(stream) + } + + return nil +} + +func parseDuration(s string) (time.Duration, error) { + if d, err := strconv.ParseFloat(s, 64); err == nil { + ts := d * float64(time.Second) + if ts > float64(math.MaxInt64) || ts < float64(math.MinInt64) { + return 0, errors.Errorf("cannot parse %q to a valid duration. It overflows int64", s) + } + return time.Duration(ts), nil + } + if d, err := model.ParseDuration(s); err == nil { + return time.Duration(d), nil + } + return 0, errors.Errorf("cannot parse %q to a valid duration", s) +} diff --git a/reader/controller/queryLabelsController.go b/reader/controller/queryLabelsController.go new file mode 100644 index 00000000..34591773 --- /dev/null +++ b/reader/controller/queryLabelsController.go @@ -0,0 +1,171 @@ +package controllerv1 + +import ( + "github.com/gorilla/mux" + "github.com/gorilla/schema" + "github.com/metrico/qryn/reader/service" + "net/http" + "strconv" + "time" +) + +type QueryLabelsController struct { + Controller + QueryLabelsService *service.QueryLabelsService +} + +type ValuesParams struct { + Start time.Time + End time.Time + Raw struct { + Start string `query:"start"` + End string `query:"end"` + } +} + +func (q *QueryLabelsController) Labels(w http.ResponseWriter, r *http.Request) { + defer tamePanic(w, r) + internalCtx, err := RunPreRequestPlugins(r) + if err != nil { + PromError(500, err.Error(), w) + return + } + params, err := ParseTimeParamsV2(r, time.Nanosecond) + if err != nil { + PromError(500, err.Error(), w) + return + } + res, err := q.QueryLabelsService.Labels(internalCtx, params.Start.UnixMilli(), params.End.UnixMilli(), 1) + if err != nil { + PromError(500, err.Error(), w) + return + } + for str := range res { + w.Write([]byte(str)) + } +} + +func (q *QueryLabelsController) Values(w http.ResponseWriter, r *http.Request) { + defer tamePanic(w, r) + internalCtx, err := RunPreRequestPlugins(r) + if err != nil { + PromError(500, err.Error(), w) + return + } + params, err := ParseLogSeriesParamsV2(r, time.Nanosecond) + if err != nil { + PromError(500, err.Error(), w) + return + } + name := mux.Vars(r)["name"] + if name == "" { + PromError(500, "label name is required", w) + return + } + res, err := q.QueryLabelsService.Values(internalCtx, name, params.Match, + params.Start.UnixMilli(), params.End.UnixMilli(), 1) + if err != nil { + PromError(500, err.Error(), w) + return + } + for str := range res { + w.Write([]byte(str)) + } +} + +type SeriesParams struct { + ValuesParams + Match []string `query:"match[]"` + Raw struct { + Match []string `query:"match[]"` + } +} + +func (q *QueryLabelsController) Series(w http.ResponseWriter, r *http.Request) { + defer tamePanic(w, r) + internalCtx, err := RunPreRequestPlugins(r) + if err != nil { + PromError(500, err.Error(), w) + return + } + params, err := ParseLogSeriesParamsV2(r, time.Nanosecond) + if len(params.Match) == 0 { + PromError(400, "match param is required", w) + return + } + if err != nil { + PromError(400, err.Error(), w) + return + } + res, err := q.QueryLabelsService.Series(internalCtx, params.Match, + params.ValuesParams.Start.UnixMilli(), params.ValuesParams.End.UnixMilli(), 1) + if err != nil { + PromError(400, err.Error(), w) + return + } + for str := range res { + w.Write([]byte(str)) + } +} + +func ParseLogSeriesParamsV2(r *http.Request, unit time.Duration) (SeriesParams, error) { + res := SeriesParams{} + var err error + res.ValuesParams, err = ParseTimeParamsV2(r, unit) + if err != nil { + return res, err + } + if r.Method == "POST" && r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" { + err = r.ParseForm() + if err != nil { + return res, err + } + for _, v := range r.Form["match[]"] { + res.Raw.Match = append(res.Raw.Match, v) + } + } + for _, v := range r.URL.Query()["match[]"] { + res.Raw.Match = append(res.Raw.Match, v) + } + res.Match = res.Raw.Match + return res, nil +} + +func ParseTimeParamsV2(r *http.Request, unit time.Duration) (ValuesParams, error) { + //TODO: Rewrite ParseTimeParams using http.Request instead of fiber.Ctx + res := ValuesParams{} + if r.Method == "POST" && r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" { + err := r.ParseForm() + if err != nil { + return res, err + } + dec := schema.NewDecoder() + err = dec.Decode(&res.Raw, r.Form) + if err != nil { + return res, err + } + } + if res.Raw.Start == "" { + res.Raw.Start = r.URL.Query().Get("start") + } + if res.Raw.End == "" { + res.Raw.End = r.URL.Query().Get("end") + } + res.Start = time.Now().Add(time.Hour * -6) + if res.Raw.Start != "" { + start, err := strconv.ParseInt(res.Raw.Start, 10, 64) + if err != nil { + return res, err + } + res.Start = time.Unix(0, 0).Add(time.Duration(start) * unit) + } + res.End = time.Now() + if res.Raw.End != "" { + end, err := strconv.ParseInt(res.Raw.End, 10, 64) + if err != nil { + return res, err + } + res.End = time.Unix(0, 0).Add(time.Duration(end) * unit) + } + return res, nil +} diff --git a/reader/controller/queryRangeController.go b/reader/controller/queryRangeController.go new file mode 100644 index 00000000..00b09c6e --- /dev/null +++ b/reader/controller/queryRangeController.go @@ -0,0 +1,222 @@ +package controllerv1 + +import ( + "context" + "github.com/gorilla/websocket" + jsoniter "github.com/json-iterator/go" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/service" + "net/http" + "strconv" + "time" + + ws "github.com/gofiber/websocket/v2" + _ "github.com/gorilla/websocket" + "github.com/metrico/qryn/reader/utils/logger" +) + +type QueryRangeController struct { + Controller + QueryRangeService *service.QueryRangeService +} + +func (q *QueryRangeController) QueryRange(w http.ResponseWriter, r *http.Request) { + defer tamePanic(w, r) + internalCtx, err := RunPreRequestPlugins(r) + if err != nil { + PromError(500, err.Error(), w) + return + } + query := r.URL.Query().Get("query") + if query == "" { + PromError(400, "query parameter is required", w) + return + } + + start, err := getRequiredFloat(r, "start", "", nil) + end, err := getRequiredFloat(r, "end", "", err) + step, err := getRequiredDuration(r, "step", "1", err) + direction := r.URL.Query().Get("direction") + //if direction == "" { + // direction = "backward" + //} + _limit := r.URL.Query().Get("limit") + limit := int64(0) + if _limit != "" { + limit, _ = strconv.ParseInt(_limit, 10, 64) + } + if err != nil { + PromError(400, err.Error(), w) + return + } + ch, err := q.QueryRangeService.QueryRange(internalCtx, query, int64(start), int64(end), int64(step*1000), + limit, direction == "forward") + if err != nil { + PromError(500, err.Error(), w) + return + } + w.Header().Set("Content-Type", "application/json") + for str := range ch { + w.Write([]byte(str.Str)) + } +} + +func (q *QueryRangeController) Query(w http.ResponseWriter, r *http.Request) { + defer tamePanic(w, r) + internalCtx, err := RunPreRequestPlugins(r) + if err != nil { + PromError(500, err.Error(), w) + return + } + query := r.URL.Query().Get("query") + if query == "" { + PromError(400, "query parameter is required", w) + return + } + if query == "vector(1)+vector(1)" { + w.Header().Set("Content-Type", "application/json") + json := jsoniter.ConfigFastest + stream := json.BorrowStream(nil) + defer json.ReturnStream(stream) + + stream.WriteObjectStart() + stream.WriteObjectField("status") + stream.WriteString("success") + stream.WriteMore() + + stream.WriteObjectField("data") + stream.WriteObjectStart() + + stream.WriteObjectField("resultType") + stream.WriteString("vector") + stream.WriteMore() + + stream.WriteObjectField("result") + stream.WriteArrayStart() + + stream.WriteObjectStart() + stream.WriteObjectField("metric") + stream.WriteEmptyObject() + stream.WriteMore() + + stream.WriteObjectField("value") + stream.WriteArrayStart() + stream.WriteInt64(time.Now().Unix()) // Unix timestamp + stream.WriteMore() + stream.WriteString("2") + stream.WriteArrayEnd() + + stream.WriteObjectEnd() // End of result object + stream.WriteArrayEnd() // End of result array + + stream.WriteObjectEnd() // End of data object + stream.WriteObjectEnd() // End of main object + + w.Write(stream.Buffer()) + //w.Write([]byte(fmt.Sprintf(`{"status": "success", "data": {"resultType": "vector", "result": [{ + // "metric": {}, + // "value": [%d, "2"] + //}]}}`, time.Now().Unix()))) + return + } + iTime, err := getRequiredI64(r, "time", "0", nil) + if err != nil { + PromError(500, err.Error(), w) + return + } + if iTime == 0 { + iTime = time.Now().UnixNano() + } + + step, err := getRequiredDuration(r, "step", "1", err) + _limit := r.URL.Query().Get("limit") + limit := int64(100) + if _limit != "" { + limit, _ = strconv.ParseInt(_limit, 10, 64) + } + if err != nil { + PromError(400, err.Error(), w) + return + } + ch, err := q.QueryRangeService.QueryInstant(internalCtx, query, iTime, int64(step*1000), + limit) + if err != nil { + PromError(500, err.Error(), w) + return + } + w.Header().Set("Content-Type", "application/json") + for str := range ch { + w.Write([]byte(str.Str)) + } +} + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, +} + +func (q *QueryRangeController) Tail(w http.ResponseWriter, r *http.Request) { + watchCtx, cancel := context.WithCancel(r.Context()) + defer cancel() + internalCtx, err := runPreWSRequestPlugins(watchCtx, r) + if err != nil { + PromError(500, err.Error(), w) + return + } + query := r.URL.Query().Get("query") + if query == "" { + logger.Error("query parameter is required") + return + } + defer cancel() + var watcher model.IWatcher + watcher, err = q.QueryRangeService.Tail(internalCtx, query) + if err != nil { + logger.Error(err) + return + } + defer func() { + go func() { + for range watcher.GetRes() { + } + }() + }() + defer watcher.Close() + con, err := upgrader.Upgrade(w, r, nil) + if err != nil { + PromError(500, err.Error(), w) + return + } + defer con.Close() + con.SetCloseHandler(func(code int, text string) error { + watcher.Close() + cancel() + return nil + }) + go func() { + _, _, err := con.ReadMessage() + for err == nil { + _, _, err = con.ReadMessage() + } + }() + pingTimer := time.NewTicker(time.Second) + defer pingTimer.Stop() + for { + select { + case <-watchCtx.Done(): + return + case <-pingTimer.C: + err := con.WriteMessage(ws.TextMessage, []byte(`{"streams":[]}`)) + if err != nil { + logger.Error(err) + return + } + case str := <-watcher.GetRes(): + err = con.WriteMessage(ws.TextMessage, []byte(str.Str)) + if err != nil { + logger.Error(err) + return + } + } + } +} diff --git a/reader/controller/tempoController.go b/reader/controller/tempoController.go new file mode 100644 index 00000000..b4c3edff --- /dev/null +++ b/reader/controller/tempoController.go @@ -0,0 +1,444 @@ +package controllerv1 + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "github.com/gorilla/mux" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/utils/unmarshal" + common "go.opentelemetry.io/proto/otlp/common/v1" + resource "go.opentelemetry.io/proto/otlp/resource/v1" + v1 "go.opentelemetry.io/proto/otlp/trace/v1" + "google.golang.org/protobuf/proto" + "net/http" + "strconv" + "time" +) + +type TempoController struct { + Controller + Service model.ITempoService +} + +func (t *TempoController) Trace(w http.ResponseWriter, r *http.Request) { + internalCtx, err := RunPreRequestPlugins(r) + if err != nil { + PromError(500, err.Error(), w) + return + } + traceId := mux.Vars(r)["traceId"] + if traceId == "" { + PromError(400, "traceId is required", w) + return + } + strStart := r.URL.Query().Get("start") + if strStart == "" { + strStart = "0" + } + start, err := strconv.ParseInt(strStart, 10, 64) + if err != nil { + start = 0 + } + strEnd := r.URL.Query().Get("end") + if strEnd == "" { + strEnd = "0" + } + end, err := strconv.ParseInt(strEnd, 10, 64) + if err != nil { + end = 0 + } + bTraceId := make([]byte, 32) + _, err = hex.Decode(bTraceId, []byte(traceId)) + if err != nil { + PromError(500, err.Error(), w) + return + } + accept := r.Header.Get("Accept") + if accept == "" { + accept = "application/json" + } + res, err := t.Service.Query(internalCtx, start*1e9, end*1e9, []byte(traceId), accept == "application/protobuf") + if err != nil { + PromError(500, err.Error(), w) + return + } + + switch accept { + case "application/protobuf": + spansByServiceName := make(map[string]*v1.ResourceSpans, 100) + for span := range res { + if _, ok := spansByServiceName[span.ServiceName]; !ok { + spansByServiceName[span.ServiceName] = &v1.ResourceSpans{ + Resource: &resource.Resource{ + Attributes: []*common.KeyValue{ + { + Key: "service.name", + Value: &common.AnyValue{ + Value: &common.AnyValue_StringValue{ + span.ServiceName, + }, + }, + }, + }, + }, + ScopeSpans: []*v1.ScopeSpans{ + {Spans: make([]*v1.Span, 0, 10)}, + }, + } + } + spansByServiceName[span.ServiceName].ScopeSpans[0].Spans = + append(spansByServiceName[span.ServiceName].ScopeSpans[0].Spans, span.Span) + spansByServiceName[span.ServiceName].ScopeSpans[0].Scope = &common.InstrumentationScope{ + Name: "N/A", + Version: "v0", + } + } + + resourceSpans := make([]*v1.ResourceSpans, 0, 10) + for _, spans := range spansByServiceName { + resourceSpans = append(resourceSpans, spans) + } + traceData := v1.TracesData{ + ResourceSpans: resourceSpans, + } + bTraceData, err := proto.Marshal(&traceData) + if err != nil { + PromError(500, err.Error(), w) + return + } + w.Write(bTraceData) + default: + w.Write([]byte(`{"resourceSpans": [{ + "resource":{"attributes":[{"key":"collector","value":{"stringValue":"qryn"}}]}, + "instrumentationLibrarySpans": [{ "spans": [`)) + i := 0 + for span := range res { + res, err := json.Marshal(unmarshal.SpanToJSONSpan(span.Span)) + if err != nil { + PromError(500, err.Error(), w) + return + } + if i != 0 { + w.Write([]byte(",")) + } + w.Write(res) + i++ + } + w.Write([]byte("]}]}]}")) + } +} + +func (t *TempoController) Echo(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("echo")) + return +} + +func (t *TempoController) Tags(w http.ResponseWriter, r *http.Request) { + internalCtx, err := RunPreRequestPlugins(r) + if err != nil { + PromError(500, err.Error(), w) + return + } + cRes, err := t.Service.Tags(internalCtx) + if err != nil { + PromError(500, err.Error(), w) + return + } + w.Write([]byte(`{"tagNames": [`)) + i := 0 + for tag := range cRes { + if i != 0 { + w.Write([]byte(",")) + } + w.Write([]byte(strconv.Quote(tag))) + i++ + } + w.Write([]byte("]}")) + return +} + +func (t *TempoController) TagsV2(w http.ResponseWriter, r *http.Request) { + var err error + internalCtx, err := RunPreRequestPlugins(r) + if err != nil { + PromError(500, err.Error(), w) + return + } + + q := r.URL.Query().Get("q") + var timespan [2]time.Time + for i, req := range [][]any{{"start", time.Unix(0, 0)}, {"end", time.Unix(0, 0)}} { + strT := r.URL.Query().Get(req[0].(string)) + if strT == "" { + timespan[i] = req[1].(time.Time) + continue + } + iT, err := strconv.ParseInt(strT, 10, 64) + if err != nil { + PromError(400, fmt.Sprintf("Invalid timestamp for %s: %v", req[0].(string), err), w) + return + } + timespan[i] = time.Unix(iT, 0) + } + + limit := 2000 + if r.URL.Query().Get("limit") != "" { + limit, err = strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil || limit <= 0 || limit > 2000 { + limit = 2000 + } + } + var cRes chan string + if timespan[0].Unix() == 0 { + cRes, err = t.Service.Tags(internalCtx) + } else { + cRes, err = t.Service.TagsV2(internalCtx, q, timespan[0], timespan[1], limit) + if err != nil { + PromError(500, err.Error(), w) + return + } + } + + var arrRes []string + for v := range cRes { + arrRes = append(arrRes, v) + } + + res := map[string]any{ + "scopes": []any{ + map[string]any{ + "name": "unscoped", + "tags": arrRes, + }, + }, + } + + bRes, err := json.Marshal(res) + if err != nil { + PromError(500, err.Error(), w) + return + } + w.Header().Set("Content-Type", "application/json") + w.Write(bRes) +} + +func (t *TempoController) ValuesV2(w http.ResponseWriter, r *http.Request) { + var err error + internalCtx, err := RunPreRequestPlugins(r) + if err != nil { + PromError(500, err.Error(), w) + return + } + q := r.URL.Query().Get("q") + var timespan [2]time.Time + for i, req := range [][]any{{"start", time.Unix(0, 0)}, {"end", time.Unix(0, 0)}} { + strT := r.URL.Query().Get(req[0].(string)) + if strT == "" { + timespan[i] = req[1].(time.Time) + continue + } + iT, err := strconv.ParseInt(strT, 10, 64) + if err != nil { + PromError(400, fmt.Sprintf("Invalid timestamp for %s: %v", req[0].(string), err), w) + return + } + timespan[i] = time.Unix(iT, 0) + } + tag := mux.Vars(r)["tag"] + + limit := 2000 + if r.URL.Query().Get("limit") != "" { + limit, err = strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil || limit <= 0 || limit > 2000 { + limit = 2000 + } + } + + var cRes chan string + + if timespan[0].Unix() == 0 { + cRes, err = t.Service.Values(internalCtx, tag) + } else { + cRes, err = t.Service.ValuesV2(internalCtx, tag, q, timespan[0], timespan[1], limit) + if err != nil { + PromError(500, err.Error(), w) + return + } + } + + var arrRes []map[string]string + for v := range cRes { + arrRes = append(arrRes, map[string]string{ + "type": "string", + "value": v, + }) + } + + res := map[string]any{"tagValues": arrRes} + + bRes, err := json.Marshal(res) + if err != nil { + PromError(500, err.Error(), w) + return + } + w.Header().Set("Content-Type", "application/json") + w.Write(bRes) +} + +func (t *TempoController) Values(w http.ResponseWriter, r *http.Request) { + internalCtx, err := RunPreRequestPlugins(r) + if err != nil { + PromError(500, err.Error(), w) + return + } + tag := mux.Vars(r)["tag"] + cRes, err := t.Service.Values(internalCtx, tag) + if err != nil { + PromError(500, err.Error(), w) + return + } + i := 0 + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + w.Write([]byte(`{"tagValues": [`)) + for val := range cRes { + if i != 0 { + w.Write([]byte(",")) + } + w.Write([]byte(strconv.Quote(val))) + i++ + } + w.Write([]byte(`]}`)) +} + +func (t *TempoController) Search(w http.ResponseWriter, r *http.Request) { + internalCtx, err := RunPreRequestPlugins(r) + if err != nil { + PromError(500, err.Error(), w) + return + } + params, err := parseTraceSearchParams(r) + if err != nil { + PromError(400, err.Error(), w) + return + } + + if params.Q != "" { + if params.Limit == 0 { + params.Limit = 20 + } + ch, err := t.Service.SearchTraceQL(internalCtx, + params.Q, params.Limit, params.Start, params.End) + if err != nil { + PromError(500, err.Error(), w) + return + } + w.WriteHeader(200) + w.Write([]byte(`{"traces": [`)) + i := 0 + for traces := range ch { + for _, trace := range traces { + if i != 0 { + w.Write([]byte(",")) + } + strTrace, _ := json.Marshal(trace) + w.Write(strTrace) + i++ + } + } + w.Write([]byte("]}")) + return + } + + resChan, err := t.Service.Search( + internalCtx, + params.Tags, + params.MinDuration.Nanoseconds(), + params.MaxDuration.Nanoseconds(), + params.Limit, + params.Start.UnixNano(), + params.End.UnixNano()) + if err != nil { + PromError(500, err.Error(), w) + return + } + w.Write([]byte(`{"traces": [`)) + i := 0 + for trace := range resChan { + bTrace, err := json.Marshal(trace) + if err != nil { + fmt.Println(err) + continue + } + if i != 0 { + w.Write([]byte(",")) + } + w.Write(bTrace) + i++ + } + w.Write([]byte("]}")) + return +} + +type traceSearchParams struct { + Q string + Tags string + MinDuration time.Duration + MaxDuration time.Duration + Limit int + Start time.Time + End time.Time +} + +func parseTraceSearchParams(r *http.Request) (*traceSearchParams, error) { + var err error + res := traceSearchParams{} + res.Q = r.URL.Query().Get("q") + res.Tags = r.URL.Query().Get("tags") + res.MinDuration, err = time.ParseDuration(orDefault(r.URL.Query().Get("minDuration"), "0")) + if err != nil { + return nil, fmt.Errorf("minDuration: %v", err) + } + res.MaxDuration, err = time.ParseDuration(orDefault(r.URL.Query().Get("maxDuration"), "0")) + if err != nil { + return nil, fmt.Errorf("maxDuration: %v", err) + } + res.Limit, err = strconv.Atoi(orDefault(r.URL.Query().Get("limit"), "10")) + if err != nil { + return nil, fmt.Errorf("limit: %v", err) + } + startS, err := strconv.Atoi(orDefault(r.URL.Query().Get("start"), "0")) + if err != nil { + return nil, fmt.Errorf("start: %v", err) + } + res.Start = time.Unix(int64(startS), 0) + if startS == 0 { + res.Start = time.Now().Add(time.Hour * -6) + } + endS, err := strconv.Atoi(orDefault(r.URL.Query().Get("end"), "0")) + if err != nil { + return nil, fmt.Errorf("end: %v", err) + } + res.End = time.Unix(int64(endS), 0) + if endS == 0 { + res.End = time.Now() + } + return &res, nil +} + +func orDefault(str string, def string) string { + if str == "" { + return def + } + return str +} + +func parseDurationNS(duration string) (int64, error) { + if duration == "" { + return 0, nil + } + durationNS, err := time.ParseDuration(duration) + return int64(durationNS), err + +} diff --git a/reader/controller/utils.go b/reader/controller/utils.go new file mode 100644 index 00000000..5311ab74 --- /dev/null +++ b/reader/controller/utils.go @@ -0,0 +1,111 @@ +package controllerv1 + +import ( + "context" + "errors" + "fmt" + "github.com/metrico/qryn/reader/plugins" + "github.com/metrico/qryn/reader/utils/logger" + "net/http" + "regexp" + "runtime/debug" + "strconv" + "time" +) + +func getRequiredFloat(ctx *http.Request, name string, def string, err error) (float64, error) { + if err != nil { + return 0, err + } + strRes := ctx.URL.Query().Get(name) + if strRes == "" { + strRes = def + } + if strRes == "" { + return 0, fmt.Errorf("%s parameter is required", name) + } + iRes, err := strconv.ParseFloat(strRes, 64) + return iRes, err +} + +func getRequiredDuration(ctx *http.Request, name string, def string, err error) (float64, error) { + if err != nil { + return 0, err + } + strRes := ctx.URL.Query().Get(name) + if strRes == "" { + strRes = def + } + if strRes == "" { + return 0, fmt.Errorf("%s parameter is required", name) + } + duration, err := parseDuration(strRes) + return float64(duration.Nanoseconds()) / 1e9, err +} + +func getRequiredI64(ctx *http.Request, name string, def string, err error) (int64, error) { + if err != nil { + return 0, err + } + strRes := ctx.URL.Query().Get(name) + if strRes == "" { + strRes = def + } + if strRes == "" { + return 0, fmt.Errorf("%s parameter is required", name) + } + iRes, err := strconv.ParseInt(strRes, 10, 64) + return iRes, err +} + +func ParseTimeSecOrRFC(raw string, def time.Time) (time.Time, error) { + if raw == "" { + return def, nil + } + if regexp.MustCompile("^[0-9.]+$").MatchString(raw) { + t, _ := strconv.ParseFloat(raw, 64) + return time.Unix(int64(t), 0), nil + } + return time.Parse(time.RFC3339, raw) +} + +func tamePanic(w http.ResponseWriter, r *http.Request) { + if err := recover(); err != nil { + logger.Error("panic:", err, " stack:", string(debug.Stack())) + logger.Error("query: ", r.URL.String()) + w.WriteHeader(500) + w.Write([]byte("Internal Server Error")) + recover() + } +} + +func RunPreRequestPlugins(r *http.Request) (context.Context, error) { + ctx := r.Context() + for _, plugin := range plugins.GetPreRequestPlugins() { + _ctx, err := plugin(ctx, r) + if err == nil { + ctx = _ctx + continue + } + if errors.Is(err, plugins.ErrPluginNotApplicable) { + continue + } + return nil, err + } + return ctx, nil +} + +func runPreWSRequestPlugins(ctx context.Context, r *http.Request) (context.Context, error) { + for _, plugin := range plugins.GetPreWSRequestPlugins() { + _ctx, err := plugin(ctx, r) + if err == nil { + ctx = _ctx + continue + } + if errors.Is(err, plugins.ErrPluginNotApplicable) { + continue + } + return nil, err + } + return ctx, nil +} diff --git a/reader/dbRegistry/registry.go b/reader/dbRegistry/registry.go new file mode 100644 index 00000000..8bc739f8 --- /dev/null +++ b/reader/dbRegistry/registry.go @@ -0,0 +1,109 @@ +package dbRegistry + +import ( + "crypto/tls" + "fmt" + "github.com/ClickHouse/clickhouse-go/v2" + "github.com/jmoiron/sqlx" + "github.com/metrico/qryn/reader/config" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/plugins" + "github.com/metrico/qryn/reader/utils/dsn" + "github.com/metrico/qryn/reader/utils/logger" + "strconv" + "time" +) + +var Registry model.IDBRegistry +var DataDBSession []model.ISqlxDB +var DatabaseNodeMap []model.DataDatabasesMap + +func Init() { + p := plugins.GetDatabaseRegistryPlugin() + if p != nil { + Registry = (*p)() + } + Registry = InitStaticRegistry() +} + +func InitStaticRegistry() model.IDBRegistry { + initDataDBSession() + if len(DataDBSession) == 0 { + panic("We don't have any active DB session configured. Please check your config") + } + dbMap := map[string]*model.DataDatabasesMap{} + for i, node := range DatabaseNodeMap { + node.Session = DataDBSession[i] + dbMap[node.Config.Node] = &node + } + return NewStaticDBRegistry(dbMap) +} + +func initDataDBSession() { + dbMap := []model.ISqlxDB{} + dbNodeMap := []model.DataDatabasesMap{} + + for _, _dbObject := range config.Cloki.Setting.DATABASE_DATA { + dbObject := _dbObject + logger.Info(fmt.Sprintf("Connecting to [%s, %s, %s, %s, %d, %d, %d]\n", dbObject.Host, dbObject.User, dbObject.Name, + dbObject.Node, dbObject.Port, dbObject.ReadTimeout, dbObject.WriteTimeout)) + + getDB := func() *sqlx.DB { + opts := &clickhouse.Options{ + TLS: nil, + Addr: []string{fmt.Sprintf("%s:%d", dbObject.Host, dbObject.Port)}, + Auth: clickhouse.Auth{ + Database: dbObject.Name, + Username: dbObject.User, + Password: dbObject.Password, + }, + DialContext: nil, + Debug: dbObject.Debug, + Settings: nil, + } + + if dbObject.Secure { + opts.TLS = &tls.Config{ + InsecureSkipVerify: true, + } + } + conn := clickhouse.OpenDB(opts) + conn.SetMaxOpenConns(dbObject.MaxOpenConn) + conn.SetMaxIdleConns(dbObject.MaxIdleConn) + conn.SetConnMaxLifetime(time.Minute * 10) + db := sqlx.NewDb(conn, "clickhouse") + db.SetMaxOpenConns(dbObject.MaxOpenConn) + db.SetMaxIdleConns(dbObject.MaxIdleConn) + db.SetConnMaxLifetime(time.Minute * 10) + return db + } + + dbMap = append(dbMap, &dsn.StableSqlxDBWrapper{ + DB: getDB(), + GetDB: getDB, + Name: _dbObject.Node, + }) + + chDsn := "n-clickhouse://" + if dbObject.ClusterName != "" { + chDsn = "c-clickhouse://" + } + chDsn += dbObject.User + ":" + dbObject.Password + "@" + dbObject.Host + + strconv.FormatInt(int64(dbObject.Port), 10) + "/" + dbObject.Name + if dbObject.Secure { + chDsn += "?secure=true" + } + + dbNodeMap = append(dbNodeMap, model.DataDatabasesMap{ + Config: &dbObject, + DSN: chDsn, + }) + + logger.Info("----------------------------------- ") + logger.Info("*** Database Config Session created *** ") + logger.Info("----------------------------------- ") + } + + DataDBSession = dbMap + DatabaseNodeMap = dbNodeMap +} diff --git a/reader/dbRegistry/staticDBRegistry.go b/reader/dbRegistry/staticDBRegistry.go new file mode 100644 index 00000000..1fcd6dc3 --- /dev/null +++ b/reader/dbRegistry/staticDBRegistry.go @@ -0,0 +1,64 @@ +package dbRegistry + +import ( + "context" + "github.com/metrico/qryn/reader/model" + "math/rand" + "sync" + "time" +) + +type staticDBRegistry struct { + databases []*model.DataDatabasesMap + rand *rand.Rand + mtx sync.Mutex + lastPingTime time.Time +} + +var _ model.IDBRegistry = &staticDBRegistry{} + +func NewStaticDBRegistry(databases map[string]*model.DataDatabasesMap) model.IDBRegistry { + res := staticDBRegistry{ + rand: rand.New(rand.NewSource(time.Now().UnixNano())), + lastPingTime: time.Now(), + } + for _, d := range databases { + res.databases = append(res.databases, d) + } + return &res +} + +func (s *staticDBRegistry) GetDB(ctx context.Context) (*model.DataDatabasesMap, error) { + s.mtx.Lock() + defer s.mtx.Unlock() + idx := s.rand.Intn(len(s.databases)) + return s.databases[idx], nil +} + +func (s *staticDBRegistry) Run() { +} + +func (s *staticDBRegistry) Stop() { +} + +func (s *staticDBRegistry) Ping() error { + if s.lastPingTime.Add(time.Second * 30).After(time.Now()) { + return nil + } + for _, v := range s.databases { + err := func(db model.ISqlxDB) error { + conn, err := v.Session.Conn(context.Background()) + if err != nil { + return err + } + defer conn.Close() + to, _ := context.WithTimeout(context.Background(), time.Second*30) + return conn.PingContext(to) + }(v.Session) + if err != nil { + return err + } + } + s.lastPingTime = time.Now() + return nil +} diff --git a/reader/logql/logql_parser/.snapshots/TestParser b/reader/logql/logql_parser/.snapshots/TestParser new file mode 100644 index 00000000..52a1414a --- /dev/null +++ b/reader/logql/logql_parser/.snapshots/TestParser @@ -0,0 +1,40 @@ +([]*logql_parser.LogQLScript) (len=38) { + (*logql_parser.LogQLScript)({test_id="${testID}"}), + (*logql_parser.LogQLScript)({test_id="${testID}",freq="2"}), + (*logql_parser.LogQLScript)({test_id="${testID}",freq="2"} |~ "2[0-9]$"), + (*logql_parser.LogQLScript)(rate ({test_id="${testID}",freq="2"} |~ "2[0-9]$"[1s])), + (*logql_parser.LogQLScript)(sum by (test_id) (rate ({test_id="${testID}"} |~ "2[0-9]$"[1s]))), + (*logql_parser.LogQLScript)(rate ({test_id="${testID}",freq="2"} |~ "2[0-9]$"[1s])), + (*logql_parser.LogQLScript)(sum by (test_id) (rate ({test_id="${testID}"} |~ "2[0-9]$"[1s]))), + (*logql_parser.LogQLScript)({test_id="${testID}_json"}| json), + (*logql_parser.LogQLScript)({test_id="${testID}_json"}| json lbl_repl = "new_lbl"), + (*logql_parser.LogQLScript)({test_id="${testID}_json"}| json lbl_repl = "new_lbl" | lbl_repl = "new_val"), + (*logql_parser.LogQLScript)({test_id="${testID}_json"}| json lbl_repl = "new_lbl" | fmt = "json"), + (*logql_parser.LogQLScript)({test_id="${testID}_json"}| json | fmt =~ "[jk]son"), + (*logql_parser.LogQLScript)({test_id="${testID}_json"}| json | lbl_repl = "REPL"), + (*logql_parser.LogQLScript)(sum_over_time ({test_id="${testID}_json"}| json | lbl_repl = "REPL" | unwrap int_lbl[3s])by (test_id,lbl_repl)), + (*logql_parser.LogQLScript)(sum_over_time ({test_id="${testID}_json"}| json lbl_int1 = "int_val" | lbl_repl = "val_repl" | unwrap lbl_int1[3s])by (test_id,lbl_repl)), + (*logql_parser.LogQLScript)({test_id="${testID}"}| line_format "{ \"str\":\"{{_entry}}\", \"freq2\": {{divide freq 2}} }"), + (*logql_parser.LogQLScript)(rate ({test_id="${testID}"}| line_format "{ \"str\":\"{{_entry}}\", \"freq2\": {{divide freq 2}} }" | json | unwrap freq2[1s])by (test_id,freq2)), + (*logql_parser.LogQLScript)({test_id="${testID}_json"}| json | json int_lbl2 = "int_val"), + (*logql_parser.LogQLScript)({test_id="${testID}_json"}| line_format "{{ divide test_id 2 }}"), + (*logql_parser.LogQLScript)(rate ({test_id="${testID}_json"}| line_format "{{ divide int_lbl 2 }}" | unwrap _entry[1s])), + (*logql_parser.LogQLScript)(sum (rate ({test_id="${testID}_json"}| json[5s]))by (test_id)), + (*logql_parser.LogQLScript)(sum (rate ({test_id="${testID}_json"}| json lbl_rrr = "lbl_repl"[5s]))by (test_id,lbl_rrr)), + (*logql_parser.LogQLScript)(sum (sum_over_time ({test_id="${testID}_json"}| json | unwrap int_val[10s])by (test_id,str_id))by (test_id)), + (*logql_parser.LogQLScript)(rate ({test_id="${testID}"}[1s])== 2), + (*logql_parser.LogQLScript)(sum (rate ({test_id="${testID}"}[1s]))by (test_id)> 4), + (*logql_parser.LogQLScript)(sum (sum_over_time ({test_id="${testID}_json"}| json | unwrap str_id[10s])by (test_id,str_id))by (test_id)> 1000), + (*logql_parser.LogQLScript)(rate ({test_id="${testID}"}| line_format "12345"[1s])== 2), + (*logql_parser.LogQLScript)({test_id="${testID}"}| freq >= 4), + (*logql_parser.LogQLScript)({test_id="${testID}_json"}| json sid = "str_id" | sid >= 598), + (*logql_parser.LogQLScript)({test_id="${testID}_json"}| json | str_id >= 598), + (*logql_parser.LogQLScript)({test_id="${testID}"}| regexp "^(?<e>[^0-9]+)[0-9]+$"), + (*logql_parser.LogQLScript)({test_id="${testID}"}| regexp "^[^0-9]+(?<e>[0-9])+$"), + (*logql_parser.LogQLScript)({test_id="${testID}"}| regexp "^[^0-9]+([0-9]+(?<e>[0-9]))$"), + (*logql_parser.LogQLScript)(first_over_time ({test_id="${testID}",freq="0.5"}| regexp "^[^0-9]+(?<e>[0-9]+)$" | unwrap e[1s])by (test_id)), + (*logql_parser.LogQLScript)({test_id="${testID}"}| freq > 1 and (freq = "4" or freq == 2 or freq > 0.5)), + (*logql_parser.LogQLScript)({test_id="${testID}_json"}| json sid = "str_id" | sid >= 598 or sid < 2 and sid > 0), + (*logql_parser.LogQLScript)({test_id="${testID}_json"}| json | str_id < 2 or str_id >= 598 and str_id > 0), + (*logql_parser.LogQLScript)({test_id="${testID}_json"}| json | drop a,b,__C__,d="e") +} diff --git a/reader/logql/logql_parser/lexer_rules v2.go b/reader/logql/logql_parser/lexer_rules v2.go new file mode 100644 index 00000000..19417a3c --- /dev/null +++ b/reader/logql/logql_parser/lexer_rules v2.go @@ -0,0 +1,44 @@ +package logql_parser + +import ( + "github.com/alecthomas/participle/v2/lexer" +) + +var LogQLLexerRulesV2 = []lexer.SimpleRule{ + {"Ocb", `\{`}, + {"Ccb", `\}`}, + + {"Ob", `\(`}, + {"Cb", `\)`}, + + {"Osb", `\[`}, + {"Csb", `\]`}, + + {"Ge", `>=`}, + {"Le", `<=`}, + {"Gt", `>`}, + {"Lt", `<`}, + {"Deq", `==`}, + + {"Comma", `,`}, + + {"Neq", `!=`}, + {"Re", `=~`}, + {"Nre", `!~`}, + {"Eq", `=`}, + + {"PipeLineFilter", `(\|=|\|~)`}, + {"Pipe", `\|`}, + {"Dot", `\.`}, + + {"Macros_function", `_[a-zA-Z0-9_]+`}, + {"Label_name", `[a-zA-Z_][a-zA-Z0-9_]*`}, + {"Quoted_string", `"([^"\\]|\\.)*"`}, + {"Ticked_string", "`([^`\\\\]|\\\\.)*`"}, + + {"Integer", "[0-9]+"}, + + {"space", `\s+`}, +} + +var LogQLLexerDefinition = lexer.MustSimple(LogQLLexerRulesV2) diff --git a/reader/logql/logql_parser/model_v2.go b/reader/logql/logql_parser/model_v2.go new file mode 100644 index 00000000..a8f53a35 --- /dev/null +++ b/reader/logql/logql_parser/model_v2.go @@ -0,0 +1,437 @@ +package logql_parser + +import ( + "encoding/json" + "fmt" + "strings" +) + +type LogQLScript struct { + StrSelector *StrSelector `@@` + LRAOrUnwrap *LRAOrUnwrap `| @@` + AggOperator *AggOperator `| @@` + Macros *MacrosOp `| @@` + TopK *TopK `| @@` + QuantileOverTime *QuantileOverTime `| @@` +} + +func (l LogQLScript) String() string { + if l.StrSelector != nil { + return l.StrSelector.String() + } + if l.LRAOrUnwrap != nil { + return l.LRAOrUnwrap.String() + } + if l.AggOperator != nil { + return l.AggOperator.String() + } + if l.Macros != nil { + return l.Macros.String() + } + if l.TopK != nil { + return l.TopK.String() + } + if l.QuantileOverTime != nil { + return l.QuantileOverTime.String() + } + return "" +} + +type StrSelector struct { + StrSelCmds []StrSelCmd `"{" @@ ("," @@ )* "}" ` + Pipelines []StrSelectorPipeline `@@*` +} + +func (l StrSelector) String() string { + sel := make([]string, len(l.StrSelCmds)) + for i, c := range l.StrSelCmds { + sel[i] = c.Label.String() + c.Op + c.Val.String() + } + ppl := make([]string, len(l.Pipelines)) + for i, p := range l.Pipelines { + ppl[i] = p.String() + } + return fmt.Sprintf("{%s}%s", + strings.Join(sel, ","), + strings.Join(ppl, " ")) +} + +type StrSelCmd struct { + Label LabelName `@@` + Op string `@("="|"!="|"=~"|"!~")` + Val QuotedString `@@` +} + +type LabelName struct { + Name string `@(Macros_function|Label_name)` +} + +func (l LabelName) String() string { + return l.Name +} + +type QuotedString struct { + Str string `@(Quoted_string|Ticked_string) ` +} + +func (q QuotedString) String() string { + return q.Str +} + +func (q *QuotedString) Unquote() (string, error) { + str := q.Str + if q.Str[0] == '`' { + str = str[1 : len(str)-1] + str = strings.ReplaceAll(str, "\\`", "`") + str = strings.ReplaceAll(str, `\`, `\\`) + str = strings.ReplaceAll(str, `"`, `\"`) + str = `"` + str + `"` + } + var res string = "" + err := json.Unmarshal([]byte(str), &res) + return res, err +} + +type StrSelectorPipeline struct { + LineFilter *LineFilter `@@ ` + LabelFilter *LabelFilter `| "|" @@ ` + Parser *Parser `| "|" @@ ` + LineFormat *LineFormat `| "|" @@ ` + LabelFormat *LabelFormat `| "|" @@ ` + Unwrap *Unwrap `| "|" @@ ` + Drop *Drop `| "|" @@ ` +} + +func (s *StrSelectorPipeline) String() string { + if s.LineFilter != nil { + return s.LineFilter.String() + } + + if s.LabelFilter != nil { + return "| " + s.LabelFilter.String() + } + + if s.Parser != nil { + return s.Parser.String() + } + + if s.LineFormat != nil { + return s.LineFormat.String() + } + + if s.LabelFormat != nil { + return s.LabelFormat.String() + } + + if s.Unwrap != nil { + return s.Unwrap.String() + } + + return s.Drop.String() +} + +type LineFilter struct { + Fn string `@("|="|"!="|"|~"|"!~")` + Val QuotedString `@@` +} + +func (l *LineFilter) String() string { + return fmt.Sprintf(" %s %s", l.Fn, l.Val.String()) +} + +type LabelFilter struct { + Head Head `@@` + Op string `(@("and"|"or"))?` + Tail *LabelFilter `@@?` +} + +func (l *LabelFilter) String() string { + bld := strings.Builder{} + bld.WriteString(l.Head.String()) + if l.Op == "" { + return bld.String() + } + bld.WriteString(" ") + bld.WriteString(l.Op) + bld.WriteString(" ") + bld.WriteString(l.Tail.String()) + return bld.String() +} + +type Head struct { + ComplexHead *LabelFilter `"(" @@ ")"` + SimpleHead *SimpleLabelFilter `|@@` +} + +func (h *Head) String() string { + if h.ComplexHead != nil { + return "(" + h.ComplexHead.String() + ")" + } + return h.SimpleHead.String() +} + +type SimpleLabelFilter struct { + Label LabelName `@@` + Fn string `@("="|"!="|"!~"|"=="|">="|">"|"<="|"<"|"=~")` + StrVal *QuotedString `(@@` + NumVal string `| @(Integer "."? Integer*))` +} + +func (s *SimpleLabelFilter) String() string { + bld := strings.Builder{} + bld.WriteString(fmt.Sprintf("%s %s ", s.Label, s.Fn)) + if s.StrVal != nil { + bld.WriteString(s.StrVal.String()) + } else { + bld.WriteString(s.NumVal) + } + return bld.String() + +} + +type Parser struct { + Fn string `@("json"|"logfmt"|"regexp")` + ParserParams []ParserParam `@@? ("," @@)*` +} + +func (p *Parser) String() string { + if p.ParserParams == nil { + return fmt.Sprintf("| %s", p.Fn) + } + params := make([]string, len(p.ParserParams)) + for i, param := range p.ParserParams { + params[i] = param.String() + } + return fmt.Sprintf("| %s %s", p.Fn, strings.Join(params, ", ")) +} + +type ParserParam struct { + Label *LabelName `(@@ "=" )?` + Val QuotedString `@@` +} + +func (p *ParserParam) String() string { + if p.Label == nil { + return p.Val.String() + } + return fmt.Sprintf("%s = %s", p.Label, p.Val.String()) +} + +type LineFormat struct { + Val QuotedString `"line_format" @@ ` +} + +func (f *LineFormat) String() string { + return fmt.Sprintf("| line_format %s", f.Val.String()) +} + +type LabelFormat struct { + LabelFormatOps []LabelFormatOp `"label_format" @@ ("," @@ )*` +} + +func (l *LabelFormat) String() string { + ops := make([]string, len(l.LabelFormatOps)) + for i, op := range l.LabelFormatOps { + ops[i] = op.String() + } + return fmt.Sprintf("| label_format %s", strings.Join(ops, ", ")) +} + +type LabelFormatOp struct { + Label LabelName `@@ "=" ` + LabelVal *LabelName `(@@` + ConstVal *QuotedString `|@@)` +} + +func (l *LabelFormatOp) String() string { + bld := strings.Builder{} + bld.WriteString(l.Label.String()) + bld.WriteString(" = ") + if l.LabelVal != nil { + bld.WriteString(l.LabelVal.String()) + } else { + bld.WriteString(l.ConstVal.String()) + } + return bld.String() +} + +type Unwrap struct { + Fn string `@("unwrap"|"unwrap_value")` + Label LabelName ` @@?` +} + +func (u *Unwrap) String() string { + return fmt.Sprintf("| %s %s", u.Fn, u.Label.String()) +} + +type Drop struct { + Fn string `@("drop")` + Params []DropParam `@@? ("," @@)*` +} + +func (d *Drop) String() string { + params := make([]string, len(d.Params)) + for i, param := range d.Params { + params[i] = param.String() + } + return fmt.Sprintf("| %s %s", d.Fn, strings.Join(params, ",")) +} + +type DropParam struct { + Label LabelName `@@` + Val *QuotedString `("=" @@)?` +} + +func (d *DropParam) String() string { + bld := strings.Builder{} + bld.WriteString(d.Label.String()) + if d.Val != nil { + bld.WriteString("=") + bld.WriteString(d.Val.String()) + } + return bld.String() +} + +type LRAOrUnwrap struct { + Fn string `@("rate"|"count_over_time"|"bytes_rate"|"bytes_over_time"|"absent_over_time"| +"sum_over_time"|"avg_over_time"|"max_over_time"|"min_over_time"|"first_over_time"|"last_over_time"| +"stdvar_over_time"|"stddev_over_time")` + ByOrWithoutPrefix *ByOrWithout `( @@)?` + StrSel StrSelector `"(" @@ ` + Time string `"[" @Integer ` + TimeUnit string `@("ns"|"us"|"ms"|"s"|"m"|"h") "]" ")" ` + ByOrWithoutSuffix *ByOrWithout `@@?` + Comparison *Comparison `@@?` +} + +func (l LRAOrUnwrap) String() string { + res := l.Fn + if l.ByOrWithoutPrefix != nil { + res += " " + l.ByOrWithoutPrefix.String() + } + res += " (" + l.StrSel.String() + "[" + l.Time + l.TimeUnit + "])" + if l.ByOrWithoutPrefix == nil && l.ByOrWithoutSuffix != nil { + res += l.ByOrWithoutSuffix.String() + } + if l.Comparison != nil { + res += l.Comparison.String() + } + return res +} + +type Comparison struct { + Fn string `@("=="|"!="|">"|">="|"<"|"<=") ` + Val string `@(Integer "."? Integer*)` +} + +func (l Comparison) String() string { + return l.Fn + " " + l.Val +} + +type ByOrWithout struct { + Fn string `@("by"|"without") ` + Labels []LabelName `"(" @@ ("," @@)* ")" ` +} + +func (l ByOrWithout) String() string { + labels := make([]string, len(l.Labels)) + for i, label := range l.Labels { + labels[i] = label.String() + } + return fmt.Sprintf("%s (%s)", l.Fn, strings.Join(labels, ",")) +} + +func (l ByOrWithout) LabelNames() []string { + labels := make([]string, len(l.Labels)) + for i, label := range l.Labels { + labels[i] = label.String() + } + return labels +} + +type AggOperator struct { + Fn string `@("sum"|"min"|"max"|"avg"|"stddev"|"stdvar"|"count") ` + ByOrWithoutPrefix *ByOrWithout `@@?` + LRAOrUnwrap LRAOrUnwrap `"(" @@ ")" ` + ByOrWithoutSuffix *ByOrWithout `@@?` + Comparison *Comparison `@@?` +} + +func (l AggOperator) String() string { + res := l.Fn + if l.ByOrWithoutPrefix != nil { + res += " " + l.ByOrWithoutPrefix.String() + } + + res += " (" + l.LRAOrUnwrap.String() + ")" + if l.ByOrWithoutPrefix == nil && l.ByOrWithoutSuffix != nil { + res += l.ByOrWithoutSuffix.String() + } + if l.Comparison != nil { + res += l.Comparison.String() + } + return res +} + +type MacrosOp struct { + Name string `@Macros_function` + Params []QuotedString `"(" @@? ("," @@)* ")"` +} + +func (l MacrosOp) String() string { + params := make([]string, len(l.Params)) + for i, p := range l.Params { + params[i] = p.String() + } + return fmt.Sprintf("%s(%s)", l.Name, strings.Join(params, ",")) +} + +type TopK struct { + Fn string `@("topk"|"bottomk")` + Param string `"(" @(Integer+ "."? Integer*) "," ` + LRAOrUnwrap *LRAOrUnwrap `(@@` + AggOperator *AggOperator `| @@` + QuantileOverTime *QuantileOverTime `| @@)")"` + Comparison *Comparison `@@?` +} + +func (l TopK) String() string { + fn := "" + cmp := "" + if l.LRAOrUnwrap != nil { + fn = l.LRAOrUnwrap.String() + } + if l.AggOperator != nil { + fn = l.AggOperator.String() + } + if l.Comparison != nil { + cmp = l.Comparison.String() + } + return fmt.Sprintf("%s(%s, %s)%s", l.Fn, l.Param, fn, cmp) +} + +type QuantileOverTime struct { + Fn string `@"quantile_over_time" ` + ByOrWithoutPrefix *ByOrWithout `@@?` + Param string `"(" @(Integer+ "."? Integer*) "," ` + StrSel StrSelector `@@` + Time string `"[" @Integer ` + TimeUnit string `@("ns"|"us"|"ms"|"s"|"m"|"h") "]" ")" ` + ByOrWithoutSuffix *ByOrWithout `@@?` + Comparison *Comparison `@@?` +} + +func (l QuantileOverTime) String() string { + res := l.Fn + if l.ByOrWithoutPrefix != nil { + res += " " + l.ByOrWithoutPrefix.String() + } + res += " (" + l.Param + ", " + l.StrSel.String() + "[" + l.Time + l.TimeUnit + "])" + if l.ByOrWithoutPrefix == nil && l.ByOrWithoutSuffix != nil { + res += l.ByOrWithoutSuffix.String() + } + if l.Comparison != nil { + res += l.Comparison.String() + } + return res +} diff --git a/reader/logql/logql_parser/parser.go b/reader/logql/logql_parser/parser.go new file mode 100644 index 00000000..355ee14f --- /dev/null +++ b/reader/logql/logql_parser/parser.go @@ -0,0 +1,141 @@ +package logql_parser + +import ( + "fmt" + "github.com/alecthomas/participle/v2" + "reflect" + "regexp" +) + +func Parse(str string) (*LogQLScript, error) { + parser, err := participle.Build[LogQLScript](participle.Lexer(LogQLLexerDefinition), participle.UseLookahead(2)) + if err != nil { + return nil, err + } + res, err := parser.ParseString("", str+" ") + return res, err +} + +func ParseSeries(str string) (*LogQLScript, error) { + promRe := regexp.MustCompile("^([a-zA-Z_]\\w*)\\s*($|\\{.+$)") + promExp := promRe.FindSubmatch([]byte(str)) + if len(promExp) > 0 { + left := string(promExp[2]) + if len(left) > 2 { + left = "," + left[1:] + } else { + left = "}" + } + str = fmt.Sprintf("{__name__=\"%s\"%s", string(promExp[1]), left) + } + parser, err := participle.Build[LogQLScript](participle.Lexer(LogQLLexerDefinition), participle.UseLookahead(2)) + if err != nil { + return nil, err + } + res, err := parser.ParseString("", str+" ") + return res, err +} + +func FindFirst[T any](node any) *T { + if n, ok := node.(*T); ok { + return n + } + if node == nil || (reflect.ValueOf(node).Kind() == reflect.Ptr && reflect.ValueOf(node).IsNil()) { + return nil + } + switch node.(type) { + case *LogQLScript: + _node := node.(*LogQLScript) + return findFirstIn[T](_node.StrSelector, _node.LRAOrUnwrap, _node.AggOperator, + _node.TopK, _node.QuantileOverTime) + case *StrSelector: + _node := node.(*StrSelector) + var children []any + for _, c := range _node.Pipelines { + children = append(children, &c) + } + for _, c := range _node.StrSelCmds { + children = append(children, &c) + } + return findFirstIn[T](children...) + case *StrSelectorPipeline: + _node := node.(*StrSelectorPipeline) + return findFirstIn[T](_node.LineFilter, _node.LabelFilter, + _node.Parser, _node.LineFormat, _node.LabelFormat, _node.Unwrap, _node.Drop) + case *LabelFilter: + _node := node.(*LabelFilter) + return findFirstIn[T](_node.Head, _node.Tail) + case *Head: + _node := node.(*Head) + return findFirstIn[T](_node.SimpleHead, _node.ComplexHead) + case *SimpleLabelFilter: + _node := node.(*SimpleLabelFilter) + return findFirstIn[T](&_node.Label, _node.StrVal) + case *Parser: + _node := node.(*Parser) + var children []any + for _, c := range _node.ParserParams { + children = append(children, &c) + } + return findFirstIn[T](children...) + case *ParserParam: + _node := node.(*ParserParam) + return findFirstIn[T](_node.Label, &_node.Val) + case *LineFormat: + _node := node.(*LineFormat) + return findFirstIn[T](&_node.Val) + case *LabelFormat: + _node := node.(*LabelFormat) + var children []any + for _, c := range _node.LabelFormatOps { + children = append(children, &c) + } + return findFirstIn[T](_node.LabelFormatOps) + case *LabelFormatOp: + _node := node.(*LabelFormatOp) + return findFirstIn[T](&_node.Label, _node.LabelVal, _node.ConstVal) + case *Unwrap: + _node := node.(*Unwrap) + return findFirstIn[T](&_node.Label) + case *Drop: + _node := node.(*Drop) + var children []any + for _, c := range _node.Params { + children = append(children, &c) + } + return findFirstIn[T](children...) + case *DropParam: + _node := node.(*DropParam) + return findFirstIn[T](_node.Val, &_node.Label) + case *LRAOrUnwrap: + _node := node.(*LRAOrUnwrap) + return findFirstIn[T](&_node.StrSel, _node.ByOrWithoutPrefix, _node.ByOrWithoutSuffix) + case *ByOrWithout: + _node := node.(*ByOrWithout) + var labels []any + for _, l := range _node.Labels { + labels = append(labels, &l) + } + return findFirstIn[T](labels...) + case *AggOperator: + _node := node.(*AggOperator) + return findFirstIn[T](&_node.LRAOrUnwrap, _node.ByOrWithoutPrefix, _node.ByOrWithoutSuffix, _node.Comparison) + case *TopK: + _node := node.(*TopK) + return findFirstIn[T](_node.AggOperator, _node.LRAOrUnwrap, _node.QuantileOverTime, _node.Comparison) + case *QuantileOverTime: + _node := node.(*QuantileOverTime) + return findFirstIn[T](&_node.StrSel, _node.Comparison, _node.ByOrWithoutPrefix, _node.ByOrWithoutSuffix) + } + return nil +} + +func findFirstIn[T any](node ...any) *T { + for _, n := range node { + res := FindFirst[T](n) + if res != nil { + return res + } + } + return nil +} diff --git a/reader/logql/logql_parser/parser_test.go b/reader/logql/logql_parser/parser_test.go new file mode 100644 index 00000000..977b8ae0 --- /dev/null +++ b/reader/logql/logql_parser/parser_test.go @@ -0,0 +1,96 @@ +package logql_parser + +import ( + "fmt" + "github.com/bradleyjkemp/cupaloy" + "testing" +) + +func TestParser(t *testing.T) { + tests := []string{ + "{test_id=\"${testID}\"}", + "{test_id=\"${testID}\", freq=\"2\"}", + "{test_id=\"${testID}\", freq=\"2\"} |~ \"2[0-9]$\"", + "rate({test_id=\"${testID}\", freq=\"2\"} |~ \"2[0-9]$\" [1s])", + "sum by (test_id) (rate({test_id=\"${testID}\"} |~ \"2[0-9]$\" [1s]))", + "rate({test_id=\"${testID}\", freq=\"2\"} |~ \"2[0-9]$\" [1s])", + "sum by (test_id) (rate({test_id=\"${testID}\"} |~ \"2[0-9]$\" [1s]))", + "{test_id=\"${testID}_json\"}|json", + "{test_id=\"${testID}_json\"}|json lbl_repl=\"new_lbl\"", + "{test_id=\"${testID}_json\"}|json lbl_repl=\"new_lbl\"|lbl_repl=\"new_val\"", + "{test_id=\"${testID}_json\"}|json lbl_repl=\"new_lbl\"|fmt=\"json\"", + "{test_id=\"${testID}_json\"}|json|fmt=~\"[jk]son\"", + "{test_id=\"${testID}_json\"}|json|lbl_repl=\"REPL\"", + "sum_over_time({test_id=\"${testID}_json\"}|json|lbl_repl=\"REPL\"|unwrap int_lbl [3s]) by (test_id, lbl_repl)", + "sum_over_time({test_id=\"${testID}_json\"}|json lbl_int1=\"int_val\"|lbl_repl=\"val_repl\"|unwrap lbl_int1 [3s]) by (test_id, lbl_repl)", + "{test_id=\"${testID}\"}| line_format \"{ \\\"str\\\":\\\"{{_entry}}\\\", \\\"freq2\\\": {{divide freq 2}} }\"", + "rate({test_id=\"${testID}\"}| line_format \"{ \\\"str\\\":\\\"{{_entry}}\\\", \\\"freq2\\\": {{divide freq 2}} }\"| json|unwrap freq2 [1s]) by (test_id, freq2)", + "{test_id=\"${testID}_json\"}|json|json int_lbl2=\"int_val\"", + "{test_id=\"${testID}_json\"}| line_format \"{{ divide test_id 2 }}\"", + "rate({test_id=\"${testID}_json\"}| line_format \"{{ divide int_lbl 2 }}\" | unwrap _entry [1s])", + "sum(rate({test_id=\"${testID}_json\"}| json [5s])) by (test_id)", + "sum(rate({test_id=\"${testID}_json\"}| json lbl_rrr=\"lbl_repl\" [5s])) by (test_id, lbl_rrr)", + "sum(sum_over_time({test_id=\"${testID}_json\"}| json | unwrap int_val [10s]) by (test_id, str_id)) by (test_id)", + "rate({test_id=\"${testID}\"} [1s]) == 2", + "sum(rate({test_id=\"${testID}\"} [1s])) by (test_id) > 4", + "sum(sum_over_time({test_id=\"${testID}_json\"}| json | unwrap str_id [10s]) by (test_id, str_id)) by (test_id) > 1000", + "rate({test_id=\"${testID}\"} | line_format \"12345\" [1s]) == 2", + "{test_id=\"${testID}\"} | freq >= 4", + "{test_id=\"${testID}_json\"} | json sid=\"str_id\" | sid >= 598", + "{test_id=\"${testID}_json\"} | json | str_id >= 598", + "{test_id=\"${testID}\"} | regexp \"^(?<e>[^0-9]+)[0-9]+$\"", + "{test_id=\"${testID}\"} | regexp \"^[^0-9]+(?<e>[0-9])+$\"", + "{test_id=\"${testID}\"} | regexp \"^[^0-9]+([0-9]+(?<e>[0-9]))$\"", + "first_over_time({test_id=\"${testID}\", freq=\"0.5\"} | regexp \"^[^0-9]+(?<e>[0-9]+)$\" | unwrap e [1s]) by(test_id)", + "{test_id=\"${testID}\"} | freq > 1 and (freq=\"4\" or freq==2 or freq > 0.5)", + "{test_id=\"${testID}_json\"} | json sid=\"str_id\" | sid >= 598 or sid < 2 and sid > 0", + "{test_id=\"${testID}_json\"} | json | str_id < 2 or str_id >= 598 and str_id > 0", + "{test_id=\"${testID}_json\"} | json | drop a, b, __C__, d=\"e\"", + } + asts := make([]*LogQLScript, len(tests)) + for i, str := range tests { + ast, err := Parse(str) + if err != nil { + fmt.Printf("[%d]: %s\n", i, str) + t.Fatal(err) + } + asts[i] = ast + } + cupaloy.SnapshotT(t, asts) +} + +func TestQuotedString_String(t *testing.T) { + res := "abcabc\" ` d" + str, err := (&QuotedString{Str: "\"abcabc\\\" ` d\""}).Unquote() + if err != nil { + t.Fatal(err) + } + if str != res { + t.Fatalf("%s != %s", str, res) + } + str, err = (&QuotedString{Str: "`abcabc\" \\` d`"}).Unquote() + if err != nil { + t.Fatal(err) + } + if str != res { + t.Fatalf("%s != %s", str, res) + } +} + +func TestParser2(t *testing.T) { + ast, err := Parse(`{sender="logtest"} |= "GET"`) + if err != nil { + t.Fatal(err) + } + + fmt.Println(ast.String()) +} + +func TestFindFirst(t *testing.T) { + ast, err := Parse(`{sender="logtest"} |= "GET"`) + if err != nil { + t.Fatal(err) + } + strSel := FindFirst[LineFilter](ast) + fmt.Println(strSel) +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/analyze.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/analyze.go new file mode 100644 index 00000000..5effbaae --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/analyze.go @@ -0,0 +1,243 @@ +package clickhouse_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_parser" + "reflect" + "time" +) + +func (p *planner) analyzeScript() { + pipeline := getPipeline(p.script) + + p.labelsJoinIdx = -1 + + p.metrics15Shortcut = AnalyzeMetrics15sShortcut(p.script) + if p.metrics15Shortcut { + return + } + + p.simpleLabelOperation = make([]bool, len(pipeline)) + for i, ppl := range pipeline { + if ppl.LabelFilter != nil { + p.simpleLabelOperation[i] = true + } + if ppl.Parser != nil { + break + } + } + + for i, ppl := range pipeline { + if ppl.Parser != nil { + p.labelsJoinIdx = i + break + } + if ppl.LabelFilter != nil && !p.simpleLabelOperation[i] { + p.labelsJoinIdx = i + break + } + if ppl.LineFormat != nil { + p.labelsJoinIdx = i + break + } + if ppl.Drop != nil { + p.labelsJoinIdx = i + break + } + } + + p.renewMainAfter = make([]bool, len(pipeline)) + for i, ppl := range pipeline { + p.renewMainAfter[i] = i < len(pipeline)-1 && + ppl.Parser != nil && pipeline[i+1].Parser == nil + } + + for _, ppl := range pipeline { + p.fastUnwrap = p.fastUnwrap && ppl.Parser == nil + } + + p.matrixFunctionsLabelsIDX = -1 + p.getFunctionOrder(p.script) + +} + +func AnalyzeMetrics15sShortcut(script *logql_parser.LogQLScript) bool { + var lraOrUnwrap = findFirst[logql_parser.LRAOrUnwrap](script) + if lraOrUnwrap == nil { + return false + } + if lraOrUnwrap.Fn != "rate" && lraOrUnwrap.Fn != "count_over_time" { + return false + } + duration, err := time.ParseDuration(lraOrUnwrap.Time + lraOrUnwrap.TimeUnit) + if err != nil { + return false + } + if duration.Seconds() < 15 { + return false + } + if lraOrUnwrap.StrSel.Pipelines != nil && + lraOrUnwrap.StrSel.Pipelines[len(lraOrUnwrap.StrSel.Pipelines)-1].Unwrap != nil { + return false + } + for _, ppl := range lraOrUnwrap.StrSel.Pipelines { + if ppl.Parser != nil { + return false + } + if ppl.Drop != nil { + return false + } + if ppl.LineFilter != nil { + str, err := ppl.LineFilter.Val.Unquote() + if str != "" || err != nil { + return false + } + } + } + return true +} + +func (p *planner) getFunctionOrder(script any) { + maybeComparison := func(op *logql_parser.Comparison) { + if op != nil { + p.matrixFunctionsOrder = append(p.matrixFunctionsOrder, func() error { + return p.planComparison(op) + }) + } + } + + switch script.(type) { + case *logql_parser.LogQLScript: + script := script.(*logql_parser.LogQLScript) + visit(p.getFunctionOrder, script.LRAOrUnwrap, script.AggOperator, script.TopK, script.QuantileOverTime) + break + case *logql_parser.LRAOrUnwrap: + script := script.(*logql_parser.LRAOrUnwrap) + if len(script.StrSel.Pipelines) > 0 && script.StrSel.Pipelines[len(script.StrSel.Pipelines)-1].Unwrap != nil { + if p.matrixFunctionsLabelsIDX == -1 { + p.matrixFunctionsLabelsIDX = len(p.matrixFunctionsOrder) + } + p.matrixFunctionsOrder = append(p.matrixFunctionsOrder, func() error { + return p.planUnwrapFn(script) + }) + } else { + p.matrixFunctionsOrder = append(p.matrixFunctionsOrder, func() error { + return p.planLRA(script) + }) + } + maybeComparison(script.Comparison) + break + case *logql_parser.AggOperator: + script := script.(*logql_parser.AggOperator) + p.getFunctionOrder(&script.LRAOrUnwrap) + if script.ByOrWithoutPrefix != nil || script.ByOrWithoutSuffix != nil && p.matrixFunctionsLabelsIDX == -1 { + p.matrixFunctionsLabelsIDX = len(p.matrixFunctionsOrder) + } + p.matrixFunctionsOrder = append(p.matrixFunctionsOrder, func() error { + return p.planAgg(script, p.matrixFunctionsLabelsIDX != -1) + }) + maybeComparison(script.Comparison) + break + case *logql_parser.TopK: + script := script.(*logql_parser.TopK) + visit(p.getFunctionOrder, script.LRAOrUnwrap, script.AggOperator, script.QuantileOverTime) + p.matrixFunctionsOrder = append(p.matrixFunctionsOrder, func() error { + return p.planTopK(script) + }) + maybeComparison(script.Comparison) + break + case *logql_parser.QuantileOverTime: + script := script.(*logql_parser.QuantileOverTime) + p.matrixFunctionsOrder = append(p.matrixFunctionsOrder, func() error { + return p.planQuantileOverTime(script) + }) + maybeComparison(script.Comparison) + break + } +} + +func visit(fn func(any), nodes ...any) { + for _, n := range nodes { + if n != nil && !reflect.ValueOf(n).IsNil() { + fn(n) + } + } +} + +func findFirst[T any](nodes ...any) *T { + for _, n := range nodes { + if n == nil || reflect.ValueOf(n).IsNil() { + continue + } + if _, ok := n.(*T); ok { + return n.(*T) + } + var res *T + switch n.(type) { + case *logql_parser.LogQLScript: + _n := n.(*logql_parser.LogQLScript) + res = findFirst[T]( + _n.LRAOrUnwrap, + _n.AggOperator, + _n.TopK, + _n.QuantileOverTime, + _n.StrSelector, + _n.Macros, + ) + case *logql_parser.StrSelector: + _n := n.(*logql_parser.StrSelector) + var children []any + for i := range _n.Pipelines { + children = append(children, &_n.Pipelines[i]) + } + for i := range _n.StrSelCmds { + children = append(children, &_n.StrSelCmds[i]) + } + res = findFirst[T](children...) + case *logql_parser.LRAOrUnwrap: + _n := n.(*logql_parser.LRAOrUnwrap) + res = findFirst[T]( + &_n.StrSel, + _n.ByOrWithoutPrefix, + _n.ByOrWithoutSuffix, + _n.Comparison, + ) + case *logql_parser.AggOperator: + _n := n.(*logql_parser.AggOperator) + res = findFirst[T](_n.ByOrWithoutPrefix, &_n.LRAOrUnwrap, _n.ByOrWithoutSuffix, _n.Comparison) + case *logql_parser.MacrosOp: + _n := n.(*logql_parser.MacrosOp) + res = findFirst[T](_n.Params) + case *logql_parser.TopK: + _n := n.(*logql_parser.TopK) + res = findFirst[T](_n.LRAOrUnwrap, _n.AggOperator, _n.Comparison, _n.QuantileOverTime) + case *logql_parser.QuantileOverTime: + _n := n.(*logql_parser.QuantileOverTime) + res = findFirst[T](&_n.StrSel, _n.Comparison, _n.ByOrWithoutPrefix, _n.ByOrWithoutSuffix) + case *logql_parser.StrSelCmd: + _n := n.(*logql_parser.StrSelCmd) + res = findFirst[T](&_n.Val, &_n.Label) + case *logql_parser.StrSelectorPipeline: + _n := n.(*logql_parser.StrSelectorPipeline) + res = findFirst[T]( + _n.Parser, + _n.LineFilter, + _n.Unwrap, + _n.LabelFilter, + _n.LineFormat, + _n.LabelFormat, + _n.Drop, + ) + case *logql_parser.ByOrWithout: + _n := n.(*logql_parser.ByOrWithout) + children := make([]any, len(_n.Labels)) + for i := range _n.Labels { + children[i] = &_n.Labels[i] + } + res = findFirst[T](children...) + } + if res != nil { + return res + } + } + return nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner.go new file mode 100644 index 00000000..71de57a1 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner.go @@ -0,0 +1,524 @@ +package clickhouse_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_parser" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "reflect" + "strconv" + "strings" + "time" +) + +func Plan(script *logql_parser.LogQLScript, finalize bool) (shared.SQLRequestPlanner, error) { + return (&planner{script: script, finalize: finalize}).plan() +} + +func PlanFingerprints(script *logql_parser.LogQLScript) (shared.SQLRequestPlanner, error) { + return (&planner{script: script}).planFingerprints() +} + +type planner struct { + script *logql_parser.LogQLScript + finalize bool + //Analyze parameters + labelsJoinIdx int + fpCache *sql.With + labelsCache *sql.With + simpleLabelOperation []bool //if a pipeline operation can be made on time_series labels + renewMainAfter []bool + fastUnwrap bool + matrixFunctionsOrder []func() error + matrixFunctionsLabelsIDX int + metrics15Shortcut bool + + //SQL Planners + fpPlanner shared.SQLRequestPlanner + samplesPlanner shared.SQLRequestPlanner +} + +func (p *planner) plan() (shared.SQLRequestPlanner, error) { + err := p.check() + if err != nil { + return nil, err + } + p.analyzeScript() + + err = p.planTS() + if err != nil { + return nil, err + } + + if p.metrics15Shortcut { + p.matrixFunctionsLabelsIDX = -1 + err = p.planMetrics15Shortcut(p.script) + if err != nil { + return nil, err + } + } else { + err = p.planSpl() + if err != nil { + return nil, err + } + + for _, f := range p.matrixFunctionsOrder { + err = f() + if err != nil { + return nil, err + } + } + } + + if p.script.StrSelector != nil { + p.samplesPlanner = &MainOrderByPlanner{[]string{"timestamp_ns"}, p.samplesPlanner} + if p.finalize { + p.samplesPlanner = &MainLimitPlanner{p.samplesPlanner} + } + } + + if p.script.StrSelector == nil { + duration, err := shared.GetDuration(p.script) + if err != nil { + return nil, err + } + p.samplesPlanner = &StepFixPlanner{ + Main: p.samplesPlanner, + Duration: duration, + } + } + + if p.labelsJoinIdx == -1 && p.matrixFunctionsLabelsIDX == -1 { + p.samplesPlanner = &LabelsJoinPlanner{ + Main: p.samplesPlanner, + Fingerprints: p.fpPlanner, + TimeSeries: NewTimeSeriesInitPlanner(), + FpCache: &p.fpCache, + } + } + + p.samplesPlanner = &MainFinalizerPlanner{ + Main: p.samplesPlanner, + IsMatrix: p.script.StrSelector == nil, + IsFinal: p.finalize, + } + + /*chGetter := &ClickhouseGetterPlanner{ + ClickhouseRequestPlanner: p.samplesPlanner, + isMatrix: p.script.StrSelector == nil, + }*/ + return p.samplesPlanner, nil +} + +func (p *planner) planMetrics15Shortcut(script any) error { + dfs := func(nodes ...any) error { + for _, n := range nodes { + if n != nil && !reflect.ValueOf(n).IsNil() { + return p.planMetrics15Shortcut(n) + } + } + return nil + } + switch script.(type) { + case *logql_parser.LogQLScript: + script := script.(*logql_parser.LogQLScript) + return dfs(script.TopK, script.AggOperator, script.LRAOrUnwrap) + case *logql_parser.TopK: + script := script.(*logql_parser.TopK) + err := dfs(script.AggOperator, script.LRAOrUnwrap) + if err != nil { + return err + } + err = p.planTopK(script) + if err != nil { + return err + } + return p.planComparison(script.Comparison) + case *logql_parser.AggOperator: + script := script.(*logql_parser.AggOperator) + err := dfs(&script.LRAOrUnwrap) + if err != nil { + return err + } + withLabels := false + if script.ByOrWithoutPrefix != nil || script.ByOrWithoutSuffix != nil { + withLabels = true + p.matrixFunctionsLabelsIDX = 0 + } + err = p.planAgg(script, withLabels) + if err != nil { + return err + } + return p.planComparison(script.Comparison) + case *logql_parser.LRAOrUnwrap: + script := script.(*logql_parser.LRAOrUnwrap) + duration, err := time.ParseDuration(script.Time + script.TimeUnit) + if err != nil { + return err + } + p.samplesPlanner = &FingerprintFilterPlanner{ + FingerprintsSelectPlanner: p.fpPlanner, + MainRequestPlanner: NewMetrics15ShortcutPlanner(script.Fn, duration), + FingerprintSelectWithCache: &p.fpCache, + } + return p.planComparison(script.Comparison) + } + return nil +} + +func (p *planner) planTS() error { + streamSelector := getStreamSelector(p.script) + var ( + labelNames []string + ops []string + values []string + ) + for _, cmd := range streamSelector.StrSelCmds { + labelNames = append(labelNames, cmd.Label.Name) + ops = append(ops, cmd.Op) + val, err := cmd.Val.Unquote() + if err != nil { + return err + } + values = append(values, val) + } + _fpPlanner := NewStreamSelectPlanner(labelNames, ops, values) + + p.fpPlanner = _fpPlanner + + for i, isSimpleLabelFilter := range p.simpleLabelOperation { + if !isSimpleLabelFilter { + continue + } + ppl := getPipeline(p.script)[i] + if ppl.LabelFilter != nil { + p.fpPlanner = &SimpleLabelFilterPlanner{ + Expr: getPipeline(p.script)[i].LabelFilter, + FPSel: p.fpPlanner, + } + } + } + + return nil +} + +func (p *planner) planSpl() error { + streamSelector := getStreamSelector(p.script) + + p.samplesPlanner = &FingerprintFilterPlanner{ + FingerprintsSelectPlanner: p.fpPlanner, + MainRequestPlanner: NewSQLMainInitPlanner(), + FingerprintSelectWithCache: &p.fpCache, + } + for i, ppl := range streamSelector.Pipelines { + if i == p.labelsJoinIdx { + p.samplesPlanner = &LabelsJoinPlanner{ + Main: &MainOrderByPlanner{[]string{"timestamp_ns"}, p.samplesPlanner}, + Fingerprints: p.fpPlanner, + TimeSeries: NewTimeSeriesInitPlanner(), + FpCache: &p.fpCache, + LabelsCache: &p.labelsCache, + } + } + var err error + if ppl.LineFormat != nil { + err = p.planLineFormat(&ppl) + } else if ppl.LabelFilter != nil { + err = p.planLabelFilter(&ppl, i) + } else if ppl.LineFilter != nil { + err = p.planLineFilter(&ppl, i) + } else if ppl.Parser != nil { + err = p.planParser(&ppl) + } else if ppl.Unwrap != nil { + err = p.planUnwrap(&ppl) + } else if ppl.Drop != nil { + err = p.planDrop(i, &ppl) + } + + if err != nil { + return err + } + + if p.renewMainAfter[i] { + p.samplesPlanner = &MainRenewPlanner{ + p.samplesPlanner, + p.labelsJoinIdx != -1 && p.labelsJoinIdx <= i, + } + } + } + + return nil +} + +func (p *planner) planDrop(i int, ppl *logql_parser.StrSelectorPipeline) error { + if p.simpleLabelOperation[i] { + return nil + } + + labels, values, err := getLabelsAndValuesFromDrop(ppl.Drop) + if err != nil { + return err + } + p.samplesPlanner = &PlannerDrop{ + Labels: labels, + Vals: values, + LabelsCache: &p.labelsCache, + fpCache: &p.fpCache, + Main: p.samplesPlanner, + } + return nil +} + +func (p *planner) planLRA(lra *logql_parser.LRAOrUnwrap) error { + duration, err := time.ParseDuration(lra.Time + lra.TimeUnit) + if err != nil { + return err + } + p.samplesPlanner = &LRAPlanner{ + Main: p.samplesPlanner, + Duration: duration, + Func: lra.Fn, + WithLabels: p.labelsJoinIdx != -1, + } + return nil +} + +func (p *planner) planUnwrapFn(lra *logql_parser.LRAOrUnwrap) error { + err := p.planByWithout(lra.ByOrWithoutPrefix, lra.ByOrWithoutSuffix) + if err != nil { + return err + } + duration, err := time.ParseDuration(lra.Time + lra.TimeUnit) + if err != nil { + return err + } + p.samplesPlanner = &UnwrapFunctionPlanner{ + Main: p.samplesPlanner, + Func: lra.Fn, + Duration: duration, + } + return nil +} + +func (p *planner) planByWithout(byWithout ...*logql_parser.ByOrWithout) error { + var _byWithout *logql_parser.ByOrWithout + for _, b := range byWithout { + if b != nil { + _byWithout = b + } + } + if _byWithout == nil { + return nil + } + labels := make([]string, len(_byWithout.Labels)) + for i, l := range _byWithout.Labels { + labels[i] = l.Name + } + + p.samplesPlanner = &ByWithoutPlanner{ + Main: p.samplesPlanner, + Labels: labels, + By: strings.ToLower(_byWithout.Fn) == "by", + UseTimeSeriesTable: p.labelsJoinIdx == -1, + LabelsCache: &p.labelsCache, + FPCache: &p.fpCache, + } + return nil +} + +func (p *planner) planAgg(agg *logql_parser.AggOperator, withLabels bool) error { + err := p.planByWithout(agg.ByOrWithoutPrefix, agg.ByOrWithoutSuffix) + if err != nil { + return err + } + p.samplesPlanner = &AggOpPlanner{ + Main: p.samplesPlanner, + Func: agg.Fn, + WithLabels: p.labelsJoinIdx != -1 || withLabels, + } + return nil +} + +func (p *planner) planTopK(topK *logql_parser.TopK) error { + _len, err := strconv.Atoi(topK.Param) + if err != nil { + return err + } + p.samplesPlanner = &TopKPlanner{ + Main: p.samplesPlanner, + Len: _len, + IsTop: topK.Fn == "topk", + } + return nil +} + +func (p *planner) planQuantileOverTime(script *logql_parser.QuantileOverTime) error { + err := p.planByWithout(script.ByOrWithoutPrefix, script.ByOrWithoutSuffix) + if err != nil { + return err + } + + param, err := strconv.ParseFloat(script.Param, 64) + if err != nil { + return err + } + + duration, err := time.ParseDuration(script.Time + script.TimeUnit) + if err != nil { + return err + } + + p.samplesPlanner = &QuantilePlanner{ + Main: p.samplesPlanner, + Param: param, + Duration: duration, + } + return nil +} + +func (p *planner) planComparison(script *logql_parser.Comparison) error { + if script == nil { + return nil + } + val, err := strconv.ParseFloat(script.Val, 64) + if err != nil { + return err + } + p.samplesPlanner = &ComparisonPlanner{ + Main: p.samplesPlanner, + Fn: script.Fn, + Param: val, + } + return nil +} + +func (p *planner) planLineFormat(ppl *logql_parser.StrSelectorPipeline) error { + val, err := ppl.LineFormat.Val.Unquote() + if err != nil { + return err + } + + p.samplesPlanner = &LineFormatPlanner{ + Main: p.samplesPlanner, + Template: val, + } + return nil + +} + +func (p *planner) planLabelFilter(ppl *logql_parser.StrSelectorPipeline, idx int) error { + if p.simpleLabelOperation[idx] { + return nil + } + p.samplesPlanner = &LabelFilterPlanner{ + Expr: ppl.LabelFilter, + Main: p.samplesPlanner, + } + return nil +} + +func (p *planner) planLineFilter(ppl *logql_parser.StrSelectorPipeline, idx int) error { + val, err := ppl.LineFilter.Val.Unquote() + if err != nil { + return err + } + p.samplesPlanner = &LineFilterPlanner{ + Op: ppl.LineFilter.Fn, + Val: val, + Main: p.samplesPlanner, + } + return nil +} + +func (p *planner) planUnwrap(ppl *logql_parser.StrSelectorPipeline) error { + p.samplesPlanner = &UnwrapPlanner{ + Main: p.samplesPlanner, + Label: ppl.Unwrap.Label.Name, + UseTimeSeriesTable: p.fastUnwrap, + labelsCache: &p.labelsCache, + fpCache: &p.fpCache, + } + return nil +} + +func (p *planner) planParser(ppl *logql_parser.StrSelectorPipeline) error { + var ( + labels []string + vals []string + ) + for _, p := range ppl.Parser.ParserParams { + label := "" + if p.Label != nil { + label = p.Label.Name + } + labels = append(labels, label) + + val, err := p.Val.Unquote() + if err != nil { + return err + } + vals = append(vals, val) + } + p.samplesPlanner = &ParserPlanner{ + Op: ppl.Parser.Fn, + labels: labels, + Vals: vals, + Main: p.samplesPlanner, + } + return nil +} + +func (p *planner) check() error { + if p.script.Macros != nil { + return &shared.NotSupportedError{"not implemented"} + } + return nil +} + +func getPipeline(script *logql_parser.LogQLScript) []logql_parser.StrSelectorPipeline { + return getStreamSelector(script).Pipelines +} + +func getStreamSelector(script *logql_parser.LogQLScript) *logql_parser.StrSelector { + if script.StrSelector != nil { + return script.StrSelector + } + if script.LRAOrUnwrap != nil { + return &script.LRAOrUnwrap.StrSel + } + if script.AggOperator != nil { + return &script.AggOperator.LRAOrUnwrap.StrSel + } + if script.TopK != nil { + if script.TopK.LRAOrUnwrap != nil { + return &script.TopK.LRAOrUnwrap.StrSel + } + if script.TopK.AggOperator != nil { + return &script.TopK.AggOperator.LRAOrUnwrap.StrSel + } + return &script.TopK.QuantileOverTime.StrSel + } + if script.QuantileOverTime != nil { + return &script.QuantileOverTime.StrSel + } + return nil +} + +func getLabelsAndValuesFromDrop(drop *logql_parser.Drop) ([]string, []string, error) { + labels := make([]string, len(drop.Params)) + vals := make([]string, len(drop.Params)) + for i, l := range drop.Params { + labels[i] = l.Label.Name + var ( + err error + val string + ) + if l.Val != nil { + val, err = l.Val.Unquote() + if err != nil { + return nil, nil, err + } + } + vals[i] = val + } + return labels, vals, nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_agg_op.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_agg_op.go new file mode 100644 index 00000000..3e25240a --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_agg_op.go @@ -0,0 +1,51 @@ +package clickhouse_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type AggOpPlanner struct { + Main shared.SQLRequestPlanner + Func string + WithLabels bool +} + +func (b *AggOpPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := b.Main.Process(ctx) + if err != nil { + return nil, err + } + + var val sql.SQLObject + switch b.Func { + case "sum": + val = sql.NewRawObject("sum(lra_main.value)") + case "min": + val = sql.NewRawObject("min(lra_main.value)") + case "max": + val = sql.NewRawObject("max(lra_main.value)") + case "avg": + val = sql.NewRawObject("avg(lra_main.value)") + case "stddev": + val = sql.NewRawObject("stddevPop(lra_main.value)") + case "stdvar": + val = sql.NewRawObject("varPop(lra_main.value)") + case "count": + val = sql.NewRawObject("count()") + } + + withMain := sql.NewWith(main, "lra_main") + + main = sql.NewSelect().With(withMain).Select( + sql.NewSimpleCol("fingerprint", "fingerprint"), + sql.NewCol(val, "value"), + sql.NewSimpleCol("lra_main.timestamp_ns", "timestamp_ns"), + sql.NewSimpleCol("''", "string"), + ).From(sql.NewWithRef(withMain)). + GroupBy(sql.NewRawObject("fingerprint"), sql.NewRawObject("timestamp_ns")) + if b.WithLabels { + main.Select(append(main.GetSelect(), sql.NewSimpleCol("any(lra_main.labels)", "labels"))...) + } + return main, nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_by_without.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_by_without.go new file mode 100644 index 00000000..3e365e18 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_by_without.go @@ -0,0 +1,133 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "strings" +) + +type ByWithoutPlanner struct { + Main shared.SQLRequestPlanner + Labels []string + By bool + UseTimeSeriesTable bool + LabelsCache **sql.With + FPCache **sql.With +} + +func (b *ByWithoutPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := b.Main.Process(ctx) + if err != nil { + return nil, err + } + if b.UseTimeSeriesTable { + return b.processTSTable(ctx, main) + } + return b.processSimple(ctx, main) +} + +func (b *ByWithoutPlanner) processSimple(ctx *shared.PlannerContext, + main sql.ISelect) (sql.ISelect, error) { + withMain := sql.NewWith(main, fmt.Sprintf("pre_by_without_%d", ctx.Id())) + return sql.NewSelect().With(withMain). + Select( + sql.NewSimpleCol("timestamp_ns", "timestamp_ns"), + sql.NewSimpleCol("cityHash64(labels)", "fingerprint"), + sql.NewCol(&byWithoutFilterCol{ + labelsCol: sql.NewRawObject(withMain.GetAlias() + ".labels"), + labels: b.Labels, + by: b.By, + }, "labels"), + sql.NewSimpleCol("string", "string"), + sql.NewSimpleCol("value", "value")). + From(sql.NewWithRef(withMain)), nil +} + +func (b *ByWithoutPlanner) processTSTable(ctx *shared.PlannerContext, + main sql.ISelect) (sql.ISelect, error) { + var labels sql.ISelect + if b.LabelsCache != nil && *b.LabelsCache != nil { + labels = sql.NewSelect().Select( + sql.NewRawObject("fingerprint"), + sql.NewSimpleCol("cityHash64(labels)", "new_fingerprint"), + sql.NewCol(&byWithoutFilterCol{ + labelsCol: sql.NewRawObject("a.labels"), + labels: b.Labels, + by: b.By, + }, "labels"), + ).From(sql.NewCol(sql.NewWithRef(*b.LabelsCache), "a")) + } else { + from, err := labelsFromScratch(ctx, *b.FPCache) + if err != nil { + return nil, err + } + cols, err := patchCol(from.GetSelect(), "labels", func(object sql.SQLObject) (sql.SQLObject, error) { + return &byWithoutFilterCol{ + labelsCol: object, + labels: b.Labels, + by: b.By, + }, nil + }) + if err != nil { + return nil, err + } + labels = from.Select(append(cols, sql.NewSimpleCol("cityHash64(labels)", "new_fingerprint"))...) + } + + withLabels := sql.NewWith(labels, fmt.Sprintf("labels_%d", ctx.Id())) + + if b.LabelsCache != nil { + *b.LabelsCache = withLabels + } + + withMain := sql.NewWith(main, fmt.Sprintf("pre_without_%d", ctx.Id())) + + joinType := "ANY LEFT " + if ctx.IsCluster { + joinType = "GLOBAL ANY LEFT " + } + + return sql.NewSelect().With(withMain, withLabels). + Select( + sql.NewSimpleCol(withLabels.GetAlias()+".new_fingerprint", "fingerprint"), + sql.NewSimpleCol(withMain.GetAlias()+".timestamp_ns", "timestamp_ns"), + sql.NewSimpleCol(withMain.GetAlias()+".value", "value"), + sql.NewSimpleCol("''", "string"), + sql.NewSimpleCol(withLabels.GetAlias()+".labels", "labels"), + ). + From(sql.NewWithRef(withMain)). + Join(sql.NewJoin(joinType, sql.NewWithRef(withLabels), + sql.Eq( + sql.NewRawObject(withMain.GetAlias()+".fingerprint"), + sql.NewRawObject(withLabels.GetAlias()+".fingerprint"), + ))), nil +} + +type byWithoutFilterCol struct { + labelsCol sql.SQLObject + labels []string + by bool +} + +func (b *byWithoutFilterCol) String(ctx *sql.Ctx, opts ...int) (string, error) { + str, err := b.labelsCol.String(ctx, opts...) + if err != nil { + return "", err + } + + sqlLabels := make([]string, len(b.labels)) + for i, label := range b.labels { + sqlLabels[i], err = sql.NewStringVal(label).String(ctx, opts...) + if err != nil { + return "", err + } + } + + fn := "IN" + if !b.by { + fn = "NOT IN" + } + + return fmt.Sprintf("mapFilter((k,v) -> k %s (%s), %s)", fn, strings.Join(sqlLabels, ","), str), nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_comparison.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_comparison.go new file mode 100644 index 00000000..6694c3a8 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_comparison.go @@ -0,0 +1,37 @@ +package clickhouse_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type ComparisonPlanner struct { + Main shared.SQLRequestPlanner + Fn string + Param float64 +} + +func (c *ComparisonPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := c.Main.Process(ctx) + if err != nil { + return nil, err + } + + var fn func(sql.SQLObject, sql.SQLObject) *sql.LogicalOp + switch c.Fn { + case ">": + fn = sql.Gt + case "<": + fn = sql.Lt + case ">=": + fn = sql.Ge + case "<=": + fn = sql.Le + case "==": + fn = sql.Eq + case "!=": + fn = sql.Neq + } + + return main.AndHaving(fn(sql.NewRawObject("value"), sql.NewFloatVal(c.Param))), nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_drop.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_drop.go new file mode 100644 index 00000000..f66d050f --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_drop.go @@ -0,0 +1,75 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "strings" +) + +type PlannerDrop struct { + Labels []string + Vals []string + UseTimeSeriesTable bool + LabelsCache **sql.With + fpCache **sql.With + Main shared.SQLRequestPlanner +} + +func (d *PlannerDrop) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := d.Main.Process(ctx) + if err != nil { + return nil, err + } + cols, err := patchCol(main.GetSelect(), "labels", func(labels sql.SQLObject) (sql.SQLObject, error) { + return &mapDropFilter{ + col: labels, + labels: d.Labels, + values: d.Vals, + }, nil + }) + if err != nil { + return nil, err + } + main.Select(cols...) + return main, nil +} + +type mapDropFilter struct { + col sql.SQLObject + labels []string + values []string +} + +func (m mapDropFilter) String(ctx *sql.Ctx, options ...int) (string, error) { + str, err := m.col.String(ctx, options...) + if err != nil { + return "", err + } + fn, err := m.genFilterFn(ctx, options...) + if err != nil { + return "", err + } + return fmt.Sprintf("mapFilter(%s, %s)", fn, str), nil +} + +func (m mapDropFilter) genFilterFn(ctx *sql.Ctx, options ...int) (string, error) { + clauses := make([]string, len(m.labels)) + for i, l := range m.labels { + quoteKey, err := sql.NewStringVal(l).String(ctx, options...) + if err != nil { + return "", err + } + if m.values[i] == "" { + clauses[i] = fmt.Sprintf("k!=%s", quoteKey) + continue + } + quoteVal, err := sql.NewStringVal(m.values[i]).String(ctx, options...) + if err != nil { + return "", err + } + clauses[i] = fmt.Sprintf("(k, v)!=(%s, %s)", quoteKey, quoteVal) + } + return fmt.Sprintf("(k,v) -> %s", + strings.Join(clauses, " and ")), nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_drop_simple.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_drop_simple.go new file mode 100644 index 00000000..e212e75b --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_drop_simple.go @@ -0,0 +1,80 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type PlannerDropSimple struct { + Labels []string + Vals []string + + LabelsCache **sql.With + FPCache **sql.With + + Main shared.SQLRequestPlanner +} + +func (d *PlannerDropSimple) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + mainReq, err := d.Main.Process(ctx) + if err != nil { + return nil, err + } + + withMain := sql.NewWith(mainReq, fmt.Sprintf("pre_drop_%d", ctx.Id())) + var labels sql.ISelect + + if d.LabelsCache != nil && *d.LabelsCache != nil { + labels = sql.NewSelect().Select( + sql.NewRawObject("fingerprint"), + sql.NewSimpleCol("cityHash64(labels)", "new_fingerprint"), + sql.NewCol(&mapDropFilter{ + col: sql.NewRawObject("a.labels"), + labels: d.Labels, + values: d.Vals, + }, "labels"), + ).From(sql.NewCol(sql.NewWithRef(withMain), "a")) + } else { + labels, err = labelsFromScratch(ctx, *d.FPCache) + if err != nil { + return nil, err + } + sel, err := patchCol(labels.GetSelect(), "labels", func(c sql.SQLObject) (sql.SQLObject, error) { + return &mapDropFilter{ + col: c, + labels: d.Labels, + values: d.Vals, + }, nil + }) + if err != nil { + return nil, err + } + sel = append(sel, sql.NewSimpleCol("cityHash64(labels)", "new_fingerprint")) + labels.Select(sel...) + } + + withLabels := sql.NewWith(labels, fmt.Sprintf("labels_%d", ctx.Id())) + + *d.LabelsCache = withLabels + + joinType := "ANY LEFT " + if ctx.IsCluster { + joinType = "GLOBAL ANY LEFT " + } + + return sql.NewSelect().With(withMain, withLabels). + Select( + sql.NewSimpleCol(withLabels.GetAlias()+".new_fingerprint", "fingerprint"), + sql.NewSimpleCol(withMain.GetAlias()+".timestamp_ns", "timestamp_ns"), + sql.NewSimpleCol(withMain.GetAlias()+".value", "value"), + sql.NewSimpleCol("''", "string"), + sql.NewSimpleCol(withLabels.GetAlias()+".labels", "labels"), + ). + From(sql.NewWithRef(withMain)). + Join(sql.NewJoin(joinType, sql.NewWithRef(withLabels), + sql.Eq( + sql.NewRawObject(withMain.GetAlias()+".fingerprint"), + sql.NewRawObject(withLabels.GetAlias()+".fingerprint"), + ))), nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_fingerprint_filter.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_fingerprint_filter.go new file mode 100644 index 00000000..62af6468 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_fingerprint_filter.go @@ -0,0 +1,26 @@ +package clickhouse_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type FingerprintFilterPlanner struct { + FingerprintsSelectPlanner shared.SQLRequestPlanner + MainRequestPlanner shared.SQLRequestPlanner + + FingerprintSelectWithCache **sql.With +} + +func (s *FingerprintFilterPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + withPlanner := WithConnectorPlanner{ + Main: s.MainRequestPlanner, + With: s.FingerprintsSelectPlanner, + Alias: "fp_sel", + ProcessFn: func(q sql.ISelect, w *sql.With) (sql.ISelect, error) { + return q.AndWhere(sql.NewIn(sql.NewRawObject("samples.fingerprint"), sql.NewWithRef(w))), nil + }, + WithCache: s.FingerprintSelectWithCache, + } + return withPlanner.Process(ctx) +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_label_filter.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_label_filter.go new file mode 100644 index 00000000..bc6c30d0 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_label_filter.go @@ -0,0 +1,179 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_parser" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "golang.org/x/exp/slices" + "strconv" +) + +type LabelFilterPlanner struct { + Expr *logql_parser.LabelFilter + Main shared.SQLRequestPlanner + MainReq sql.ISelect + LabelValGetter func(string) sql.SQLObject +} + +func (s *LabelFilterPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main := s.MainReq + if main == nil { + var err error + main, err = s.Main.Process(ctx) + if err != nil { + return nil, err + } + } + + cond, err := s.makeSqlCond(ctx, s.Expr) + if err != nil { + return nil, err + } + return main.AndWhere(cond), nil +} + +func (s *LabelFilterPlanner) makeSqlCond(ctx *shared.PlannerContext, + expr *logql_parser.LabelFilter) (sql.SQLCondition, error) { + var ( + leftSide sql.SQLCondition + rightSide sql.SQLCondition + err error + ) + if expr.Head.SimpleHead != nil { + leftSide, err = s.makeSimpleSqlCond(ctx, expr.Head.SimpleHead) + } else { + leftSide, err = s.makeSqlCond(ctx, expr.Head.ComplexHead) + } + if err != nil { + return nil, err + } + if expr.Tail == nil { + return leftSide, nil + } + + rightSide, err = s.makeSqlCond(ctx, expr.Tail) + if err != nil { + return nil, err + } + switch expr.Op { + case "and": + return sql.And(leftSide, rightSide), nil + case "or": + return sql.Or(leftSide, rightSide), nil + } + return nil, fmt.Errorf("illegal expression " + expr.String()) +} + +func (s *LabelFilterPlanner) makeSimpleSqlCond(ctx *shared.PlannerContext, + expr *logql_parser.SimpleLabelFilter) (sql.SQLCondition, error) { + isNumeric := slices.Contains([]string{"==", ">", ">=", "<", "<="}, expr.Fn) || + (expr.Fn == "!=" && expr.StrVal == nil) + + if isNumeric { + return s.makeSimpleNumSqlCond(ctx, expr) + } + return s.makeSimpleStrSqlCond(ctx, expr) +} + +func (s *LabelFilterPlanner) makeSimpleStrSqlCond(ctx *shared.PlannerContext, + expr *logql_parser.SimpleLabelFilter) (sql.SQLCondition, error) { + var label sql.SQLObject = sql.NewRawObject(fmt.Sprintf("labels['%s']", expr.Label.Name)) + if s.LabelValGetter != nil { + label = s.LabelValGetter(expr.Label.Name) + } + + var sqlOp func(left sql.SQLObject, right sql.SQLObject) *sql.LogicalOp + switch expr.Fn { + case "=": + sqlOp = sql.Eq + case "=~": + sqlOp = func(left sql.SQLObject, right sql.SQLObject) *sql.LogicalOp { + return sql.Eq(&sqlMatch{col: left, patternObj: right}, sql.NewIntVal(1)) + } + case "!~": + sqlOp = func(left sql.SQLObject, right sql.SQLObject) *sql.LogicalOp { + return sql.Eq(&sqlMatch{col: left, patternObj: right}, sql.NewIntVal(0)) + } + case "!=": + sqlOp = sql.Neq + } + + if expr.StrVal == nil || sqlOp == nil { + return nil, fmt.Errorf("illegal expression: " + expr.String()) + } + + val, err := expr.StrVal.Unquote() + if err != nil { + return nil, err + } + return sqlOp(label, sql.NewStringVal(val)), nil +} + +func (s *LabelFilterPlanner) makeSimpleNumSqlCond(ctx *shared.PlannerContext, + expr *logql_parser.SimpleLabelFilter) (sql.SQLCondition, error) { + var label sql.SQLObject = sql.NewRawObject(fmt.Sprintf("labels['%s']", expr.Label.Name)) + if s.LabelValGetter != nil { + label = s.LabelValGetter(expr.Label.Name) + } + label = &toFloat64OrNull{label} + + var sqlOp func(left sql.SQLObject, right sql.SQLObject) *sql.LogicalOp + + switch expr.Fn { + case "==": + sqlOp = sql.Eq + case "!=": + sqlOp = sql.Neq + case ">": + sqlOp = sql.Gt + case ">=": + sqlOp = sql.Ge + case "<": + sqlOp = sql.Lt + case "<=": + sqlOp = sql.Le + } + + if expr.NumVal == "" { + return nil, fmt.Errorf("illegal expression: " + expr.String()) + } + val, err := strconv.ParseFloat(expr.NumVal, 64) + if err != nil { + return nil, err + } + return sql.And( + ¬Null{label}, + sqlOp(label, sql.NewFloatVal(val))), nil +} + +type notNull struct { + main sql.SQLObject +} + +func (t *notNull) GetFunction() string { + return "IS NOT NULL" +} +func (t *notNull) GetEntity() []sql.SQLObject { + return []sql.SQLObject{t.main} +} + +func (t *notNull) String(ctx *sql.Ctx, opts ...int) (string, error) { + str, err := t.main.String(ctx, opts...) + if err != nil { + return "", err + } + return fmt.Sprintf("%s IS NOT NULL", str), nil +} + +type toFloat64OrNull struct { + main sql.SQLObject +} + +func (t *toFloat64OrNull) String(ctx *sql.Ctx, opts ...int) (string, error) { + str, err := t.main.String(ctx, opts...) + if err != nil { + return "", err + } + return fmt.Sprintf("toFloat64OrNull(%s)", str), nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_label_format.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_label_format.go new file mode 100644 index 00000000..eaa2e12d --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_label_format.go @@ -0,0 +1,99 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_parser" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type LabelFormatPlanner struct { + Main shared.SQLRequestPlanner + Expr *logql_parser.LabelFormat + + formatters []*LineFormatPlanner +} + +func (s *LabelFormatPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := s.Main.Process(ctx) + if err != nil { + return nil, err + } + + err = s.makeFormatters() + if err != nil { + return nil, err + } + + keys := make([]sql.SQLObject, len(s.Expr.LabelFormatOps)) + for i, o := range s.Expr.LabelFormatOps { + keys[i] = sql.NewStringVal(o.Label.Name) + } + + vals := make([]sql.SQLObject, len(s.Expr.LabelFormatOps)) + for i, o := range s.Expr.LabelFormatOps { + if s.formatters[i] != nil { + err = s.formatters[i].ProcessTpl(ctx) + if err != nil { + return nil, err + } + vals[i] = &sqlFormat{ + format: s.formatters[i].formatStr, + args: s.formatters[i].args, + } + continue + } + + vals[i] = sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + lbl, err := sql.NewStringVal(o.LabelVal.Name).String(ctx, options...) + return fmt.Sprintf("labels[%s]", lbl), err + }) + } + + cols, err := patchCol(main.GetSelect(), "labels", func(object sql.SQLObject) (sql.SQLObject, error) { + return &sqlMapUpdate{ + m1: object, + m2: &sqlMapInit{ + TypeName: "Map(String, String)", + Keys: keys, + Values: vals, + }, + }, nil + }) + + return main.Select(cols...), err +} + +func (s *LabelFormatPlanner) IsSupported() bool { + err := s.makeFormatters() + if err != nil { + return false + } + + for _, f := range s.formatters { + if f != nil && !f.IsSupported() { + return false + } + } + return true +} + +func (s *LabelFormatPlanner) makeFormatters() error { + if s.formatters != nil { + return nil + } + s.formatters = make([]*LineFormatPlanner, len(s.Expr.LabelFormatOps)) + for i, op := range s.Expr.LabelFormatOps { + if op.ConstVal == nil { + continue + } + + val, err := op.ConstVal.Unquote() + if err != nil { + return err + } + + s.formatters[i] = &LineFormatPlanner{Template: val} + } + return nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_labels_joiner.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_labels_joiner.go new file mode 100644 index 00000000..cddc922f --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_labels_joiner.go @@ -0,0 +1,58 @@ +package clickhouse_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type LabelsJoinPlanner struct { + Main shared.SQLRequestPlanner + Fingerprints shared.SQLRequestPlanner + TimeSeries shared.SQLRequestPlanner + FpCache **sql.With + LabelsCache **sql.With +} + +func (l *LabelsJoinPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + tsReq, err := (&WithConnectorPlanner{ + Main: l.TimeSeries, + With: l.Fingerprints, + Alias: "fp_sel", + WithCache: l.FpCache, + + ProcessFn: func(q sql.ISelect, w *sql.With) (sql.ISelect, error) { + return q.AndPreWhere(sql.NewIn(sql.NewRawObject("time_series.fingerprint"), sql.NewWithRef(w))), nil + }, + }).Process(ctx) + if err != nil { + return nil, err + } + mainReq, err := l.Main.Process(ctx) + if err != nil { + return nil, err + } + withMain := sql.NewWith(mainReq, "main") + withTS := sql.NewWith(tsReq, "_time_series") + if l.LabelsCache != nil { + *l.LabelsCache = withTS + } + + joinType := "ANY LEFT " + if ctx.IsCluster { + joinType = "GLOBAL ANY LEFT " + } + + return sql.NewSelect(). + With(withMain, withTS). + Select( + sql.NewSimpleCol("main.fingerprint", "fingerprint"), + sql.NewSimpleCol("main.timestamp_ns", "timestamp_ns"), + sql.NewSimpleCol("_time_series.labels", "labels"), + sql.NewSimpleCol("main.string", "string"), + sql.NewSimpleCol("main.value", "value")). + From(sql.NewWithRef(withMain)). + Join(sql.NewJoin( + joinType, + sql.NewWithRef(withTS), + sql.Eq(sql.NewRawObject("main.fingerprint"), sql.NewRawObject("_time_series.fingerprint")))), nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_line_filter.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_line_filter.go new file mode 100644 index 00000000..45e3ee16 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_line_filter.go @@ -0,0 +1,103 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "regexp/syntax" + "strings" +) + +type LineFilterPlanner struct { + Op string + Val string + Main shared.SQLRequestPlanner +} + +func (l *LineFilterPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + req, err := l.Main.Process(ctx) + if err != nil { + return nil, err + } + + var clause sql.SQLCondition + switch l.Op { + case "|=": + clause, err = l.doLike("like") + break + case "!=": + clause, err = l.doLike("notLike") + break + case "|~": + likeStr, isInsensitive, isLike := l.re2Like() + if isLike { + l.Val = likeStr + like := "like" + if isInsensitive { + like = "ilike" + } + clause, err = l.doLike(like) + } else { + clause = sql.Eq(&sqlMatch{ + col: sql.NewRawObject("string"), + pattern: l.Val, + }, sql.NewIntVal(1)) + } + break + case "!~": + likeStr, isInsensitive, isLike := l.re2Like() + if isLike { + l.Val = likeStr + like := "notLike" + if isInsensitive { + like = "notILike" + } + clause, err = l.doLike(like) + } else { + clause = sql.Eq(&sqlMatch{ + col: sql.NewRawObject("string"), + pattern: l.Val, + }, sql.NewIntVal(1)) + } + break + default: + err = &shared.NotSupportedError{fmt.Sprintf("%s not supported", l.Op)} + } + + if err != nil { + return nil, err + } + return req.AndWhere(clause), nil +} + +func (l *LineFilterPlanner) doLike(likeOp string) (sql.SQLCondition, error) { + enqVal, err := l.enquoteStr(l.Val) + if err != nil { + return nil, err + } + enqVal = strings.Trim(enqVal, `'`) + enqVal = strings.Replace(enqVal, "%", "\\%", -1) + enqVal = strings.Replace(enqVal, "_", "\\_", -1) + return sql.Eq( + sql.NewRawObject(fmt.Sprintf("%s(samples.string, '%%%s%%')", likeOp, enqVal)), sql.NewIntVal(1), + ), nil +} + +func (l *LineFilterPlanner) enquoteStr(str string) (string, error) { + return sql.NewStringVal(str).String(&sql.Ctx{ + Params: map[string]sql.SQLObject{}, + Result: map[string]sql.SQLObject{}, + }) +} + +func (l *LineFilterPlanner) re2Like() (string, bool, bool) { + exp, err := syntax.Parse(l.Val, syntax.PerlX) + if err != nil { + return "", false, false + } + if exp.Op != syntax.OpLiteral || exp.Flags & ^(syntax.PerlX|syntax.FoldCase) != 0 { + return "", false, false + } + + return string(exp.Rune), exp.Flags&syntax.FoldCase != 0, true +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_line_format.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_line_format.go new file mode 100644 index 00000000..1e12cee0 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_line_format.go @@ -0,0 +1,123 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "text/template" + "text/template/parse" +) + +type LineFormatPlanner struct { + Main shared.SQLRequestPlanner + Template string + + formatStr string + args []sql.SQLObject +} + +func (l *LineFormatPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := l.Main.Process(ctx) + if err != nil { + return nil, err + } + + err = l.ProcessTpl(ctx) + if err != nil { + return nil, err + } + + sel, err := patchCol(main.GetSelect(), "string", func(object sql.SQLObject) (sql.SQLObject, error) { + return &sqlFormat{ + format: l.formatStr, + args: l.args, + }, nil + }) + if err != nil { + return nil, err + } + return main.Select(sel...), nil +} + +func (l *LineFormatPlanner) ProcessTpl(ctx *shared.PlannerContext) error { + tpl, err := template.New(fmt.Sprintf("tpl%d", ctx.Id())).Parse(l.Template) + if err != nil { + return err + } + + return l.visitNodes(tpl.Root, l.node) +} + +func (l *LineFormatPlanner) IsSupported() bool { + tpl, err := template.New("tpl1").Parse(l.Template) + if err != nil { + return false + } + err = l.visitNodes(tpl.Root, func(n parse.Node) error { + switch n.Type() { + case parse.NodeList: + break + case parse.NodeAction: + if len(n.(*parse.ActionNode).Pipe.Cmds) > 1 || len(n.(*parse.ActionNode).Pipe.Cmds[0].Args) > 1 { + return fmt.Errorf("not supported") + } + break + case parse.NodeField: + break + case parse.NodeText: + break + default: + return fmt.Errorf("not supported") + } + return nil + }) + return err == nil +} + +func (l *LineFormatPlanner) visitNodes(n parse.Node, fn func(n parse.Node) error) error { + err := fn(n) + if err != nil { + return err + } + switch n.Type() { + case parse.NodeList: + for _, _n := range n.(*parse.ListNode).Nodes { + err := l.visitNodes(_n, fn) + if err != nil { + return err + } + } + case parse.NodeAction: + for _, cmd := range n.(*parse.ActionNode).Pipe.Cmds { + for _, arg := range cmd.Args { + err := l.visitNodes(arg, fn) + if err != nil { + return err + } + } + } + } + return nil +} + +func (l *LineFormatPlanner) node(n parse.Node) error { + switch n.Type() { + case parse.NodeText: + l.textNode(n) + case parse.NodeField: + l.fieldNode(n) + } + return nil +} + +func (l *LineFormatPlanner) textNode(n parse.Node) { + l.formatStr += string(n.(*parse.TextNode).Text) +} + +func (l *LineFormatPlanner) fieldNode(n parse.Node) { + l.formatStr += fmt.Sprintf("{%d}", len(l.args)) + l.args = append(l.args, sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + lbl, err := sql.NewStringVal(n.(*parse.FieldNode).Ident[0]).String(ctx, options...) + return fmt.Sprintf("labels[%s]", lbl), err + })) +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_lra.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_lra.go new file mode 100644 index 00000000..d2c1c739 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_lra.go @@ -0,0 +1,70 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "time" +) + +type LRAPlanner struct { + Main shared.SQLRequestPlanner + Duration time.Duration + Func string + WithLabels bool +} + +func (l *LRAPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := l.Main.Process(ctx) + if err != nil { + return nil, err + } + + cols := main.GetSelect() + for i, c := range cols { + _c, ok := c.(sql.Aliased) + if !ok { + continue + } + if _c.GetAlias() == "string" { + cols[i] = sql.NewCol(_c.GetExpr(), "_string") + } + } + + var col sql.SQLObject + switch l.Func { + case "rate": + col = sql.NewRawObject(fmt.Sprintf("toFloat64(COUNT()) / %f", + float64(l.Duration.Milliseconds())/1000)) + break + case "count_over_time": + col = sql.NewRawObject("toFloat64(COUNT())") + break + case "bytes_rate": + col = sql.NewRawObject(fmt.Sprintf("toFloat64(sum(length(_string))) / %f", + float64(l.Duration.Milliseconds())/1000)) + break + case "bytes_over_time": + col = sql.NewRawObject(fmt.Sprintf("toFloat64(sum(length(_string))) / %f", + float64(l.Duration.Milliseconds())/1000)) + break + } + + withAgg := sql.NewWith(main, "agg_a") + res := sql.NewSelect().With(withAgg). + Select( + sql.NewSimpleCol( + fmt.Sprintf("intDiv(time_series.timestamp_ns, %d) * %[1]d", l.Duration.Nanoseconds()), + "timestamp_ns", + ), + sql.NewSimpleCol("fingerprint", "fingerprint"), + sql.NewSimpleCol(`''`, "string"), + sql.NewCol(col, "value"), + ). + From(sql.NewCol(sql.NewWithRef(withAgg), "time_series")). + GroupBy(sql.NewRawObject("fingerprint"), sql.NewRawObject("timestamp_ns")) + if l.WithLabels { + res.Select(append(res.GetSelect(), sql.NewSimpleCol("any(labels)", "labels"))...) + } + return res, nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_main_finalizer.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_main_finalizer.go new file mode 100644 index 00000000..3ea40edf --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_main_finalizer.go @@ -0,0 +1,73 @@ +package clickhouse_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type MainFinalizerPlanner struct { + Main shared.SQLRequestPlanner + IsMatrix bool + IsFinal bool + Alias string +} + +func (m *MainFinalizerPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + req, err := m.Main.Process(ctx) + if err != nil { + return nil, err + } + + if !ctx.CHFinalize { + return req, nil + } + + if m.Alias == "" { + m.Alias = "prefinal" + } + + if m.IsMatrix { + return m.processMatrix(ctx, req) + } + + dir := sql.ORDER_BY_DIRECTION_DESC + if ctx.OrderASC { + dir = sql.ORDER_BY_DIRECTION_ASC + } + + orderBy := []sql.SQLObject{ + sql.NewOrderBy(sql.NewRawObject("timestamp_ns"), dir), + } + if m.IsFinal { + orderBy = []sql.SQLObject{ + sql.NewOrderBy(sql.NewRawObject("fingerprint"), dir), + sql.NewOrderBy(sql.NewRawObject("timestamp_ns"), dir), + } + } + + withReq := sql.NewWith(req, m.Alias) + return sql.NewSelect(). + With(withReq). + Select( + sql.NewSimpleCol(m.Alias+".fingerprint", "fingerprint"), + sql.NewSimpleCol(m.Alias+".labels", "labels"), + sql.NewSimpleCol(m.Alias+".string", "string"), + sql.NewSimpleCol(m.Alias+".timestamp_ns", "timestamp_ns")). + From(sql.NewWithRef(withReq)). + OrderBy(orderBy...), nil +} + +func (m *MainFinalizerPlanner) processMatrix(ctx *shared.PlannerContext, req sql.ISelect) (sql.ISelect, error) { + withReq := sql.NewWith(req, m.Alias) + return sql.NewSelect(). + With(withReq). + Select( + sql.NewSimpleCol(m.Alias+".fingerprint", "fingerprint"), + sql.NewSimpleCol(m.Alias+".labels", "labels"), + sql.NewSimpleCol(m.Alias+".value", "value"), + sql.NewSimpleCol(m.Alias+".timestamp_ns", "timestamp_ns")). + From(sql.NewWithRef(withReq)). + OrderBy( + sql.NewOrderBy(sql.NewRawObject("fingerprint"), sql.ORDER_BY_DIRECTION_ASC), + sql.NewOrderBy(sql.NewRawObject("timestamp_ns"), sql.ORDER_BY_DIRECTION_ASC)), nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_main_init.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_main_init.go new file mode 100644 index 00000000..495e5103 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_main_init.go @@ -0,0 +1,32 @@ +package clickhouse_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/plugins" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type SqlMainInitPlanner struct { +} + +func NewSQLMainInitPlanner() shared.SQLRequestPlanner { + p := plugins.GetSqlMainInitPlannerPlugin() + if p != nil { + return (*p)() + } + return &SqlMainInitPlanner{} +} + +func (s *SqlMainInitPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + return sql.NewSelect(). + Select( + sql.NewSimpleCol("samples.timestamp_ns", "timestamp_ns"), + sql.NewSimpleCol("samples.fingerprint", "fingerprint"), + sql.NewSimpleCol("samples.string", "string"), + sql.NewSimpleCol("toFloat64(0)", "value"), + ).From(sql.NewSimpleCol(ctx.SamplesTableName, "samples")). + AndPreWhere( + sql.Ge(sql.NewRawObject("samples.timestamp_ns"), sql.NewIntVal(ctx.From.UnixNano())), + sql.Lt(sql.NewRawObject("samples.timestamp_ns"), sql.NewIntVal(ctx.To.UnixNano())), + GetTypes(ctx)), nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_main_limit.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_main_limit.go new file mode 100644 index 00000000..29c8c287 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_main_limit.go @@ -0,0 +1,21 @@ +package clickhouse_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type MainLimitPlanner struct { + Main shared.SQLRequestPlanner +} + +func (m *MainLimitPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + req, err := m.Main.Process(ctx) + if err != nil { + return nil, err + } + if ctx.Limit == 0 { + return req, nil + } + return req.Limit(sql.NewIntVal(ctx.Limit)), nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_main_order_by.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_main_order_by.go new file mode 100644 index 00000000..3163e9f7 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_main_order_by.go @@ -0,0 +1,29 @@ +package clickhouse_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type MainOrderByPlanner struct { + Cols []string + Main shared.SQLRequestPlanner +} + +func (m *MainOrderByPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + req, err := m.Main.Process(ctx) + if err != nil { + return nil, err + } + + dir := sql.ORDER_BY_DIRECTION_DESC + if ctx.OrderASC { + dir = sql.ORDER_BY_DIRECTION_ASC + } + + cols := make([]sql.SQLObject, len(m.Cols)) + for i, c := range m.Cols { + cols[i] = sql.NewOrderBy(sql.NewRawObject(c), dir) + } + return req.OrderBy(cols...), nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_main_renew.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_main_renew.go new file mode 100644 index 00000000..737e200b --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_main_renew.go @@ -0,0 +1,37 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type MainRenewPlanner struct { + Main shared.SQLRequestPlanner + UseLabels bool +} + +func (m *MainRenewPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := m.Main.Process(ctx) + if err != nil { + return nil, err + } + + withMain := sql.NewWith(main, fmt.Sprintf("subsel_%d", ctx.Id())) + + req := sql.NewSelect(). + With(withMain). + Select( + sql.NewSimpleCol("samples.timestamp_ns", "timestamp_ns"), + sql.NewSimpleCol("samples.fingerprint", "fingerprint")). + From(sql.NewCol(sql.NewWithRef(withMain), "samples")) + + if m.UseLabels { + req.Select(append(req.GetSelect(), sql.NewSimpleCol("samples.labels", "labels"))...) + } + + req.Select(append(req.GetSelect(), + sql.NewSimpleCol("samples.string", "string"), + sql.NewSimpleCol("samples.value", "value"))...) + return req, nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_metrics15s_shortcut.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_metrics15s_shortcut.go new file mode 100644 index 00000000..01174a7e --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_metrics15s_shortcut.go @@ -0,0 +1,226 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/plugins" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "time" +) + +type Metrics15ShortcutPlanner struct { + Function string + Duration time.Duration +} + +func NewMetrics15ShortcutPlanner(function string, duration time.Duration) shared.SQLRequestPlanner { + p := plugins.GetMetrics15ShortcutPlannerPlugin() + if p != nil { + return (*p)(function, duration) + } + return &Metrics15ShortcutPlanner{ + Function: function, + Duration: duration, + } +} + +func (m *Metrics15ShortcutPlanner) GetQuery(ctx *shared.PlannerContext, col sql.SQLObject, table string) sql.ISelect { + return sql.NewSelect(). + Select( + sql.NewSimpleCol( + fmt.Sprintf("intDiv(samples.timestamp_ns, %d) * %[1]d", m.Duration.Nanoseconds()), + "timestamp_ns", + ), + sql.NewSimpleCol("fingerprint", "fingerprint"), + sql.NewSimpleCol(`''`, "string"), + sql.NewCol(col, "value")). + From(sql.NewSimpleCol(table, "samples")). + AndWhere( + sql.Ge(sql.NewRawObject("samples.timestamp_ns"), + sql.NewIntVal(ctx.From.UnixNano()/15000000000*15000000000)), + sql.Lt(sql.NewRawObject("samples.timestamp_ns"), + sql.NewIntVal((ctx.To.UnixNano()/15000000000)*15000000000)), + GetTypes(ctx)). + GroupBy(sql.NewRawObject("fingerprint"), sql.NewRawObject("timestamp_ns")) +} + +func (m *Metrics15ShortcutPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + var col sql.SQLObject + switch m.Function { + case "rate": + col = sql.NewRawObject( + fmt.Sprintf("toFloat64(countMerge(count)) / %f", + float64(m.Duration.Milliseconds())/1000)) + case "count_over_time": + col = sql.NewRawObject("countMerge(count)") + } + v1 := m.GetQuery(ctx, col, ctx.Metrics15sTableName) + return v1, nil +} + +type UnionSelect struct { + MainSelect sql.ISelect + SubSelects []sql.ISelect +} + +func (u *UnionSelect) Distinct(distinct bool) sql.ISelect { + u.MainSelect.Distinct(distinct) + return u +} + +func (u *UnionSelect) GetDistinct() bool { + return u.MainSelect.GetDistinct() +} + +func (u *UnionSelect) Select(cols ...sql.SQLObject) sql.ISelect { + u.MainSelect.Select(cols...) + return u +} + +func (u *UnionSelect) GetSelect() []sql.SQLObject { + return u.MainSelect.GetSelect() +} + +func (u *UnionSelect) From(table sql.SQLObject) sql.ISelect { + u.MainSelect.From(table) + return u +} + +func (u *UnionSelect) GetFrom() sql.SQLObject { + return u.MainSelect.GetFrom() +} + +func (u *UnionSelect) AndWhere(clauses ...sql.SQLCondition) sql.ISelect { + for _, s := range u.SubSelects { + s.AndWhere(clauses...) + } + return u +} + +func (u *UnionSelect) OrWhere(clauses ...sql.SQLCondition) sql.ISelect { + u.MainSelect.OrWhere(clauses...) + return u +} + +func (u *UnionSelect) GetWhere() sql.SQLCondition { + return u.MainSelect.GetWhere() +} + +func (u *UnionSelect) AndPreWhere(clauses ...sql.SQLCondition) sql.ISelect { + u.MainSelect.AndPreWhere(clauses...) + return u +} + +func (u *UnionSelect) OrPreWhere(clauses ...sql.SQLCondition) sql.ISelect { + u.MainSelect.OrPreWhere(clauses...) + return u +} + +func (u *UnionSelect) GetPreWhere() sql.SQLCondition { + return u.MainSelect.GetPreWhere() +} + +func (u *UnionSelect) AndHaving(clauses ...sql.SQLCondition) sql.ISelect { + u.MainSelect.AndHaving(clauses...) + return u +} + +func (u *UnionSelect) OrHaving(clauses ...sql.SQLCondition) sql.ISelect { + u.MainSelect.OrHaving(clauses...) + return u +} + +func (u *UnionSelect) GetHaving() sql.SQLCondition { + return u.MainSelect.GetHaving() +} + +func (u *UnionSelect) SetHaving(having sql.SQLCondition) sql.ISelect { + u.MainSelect.SetHaving(having) + return u +} + +func (u *UnionSelect) GroupBy(fields ...sql.SQLObject) sql.ISelect { + u.MainSelect.GroupBy(fields...) + return u +} + +func (u *UnionSelect) GetGroupBy() []sql.SQLObject { + return u.MainSelect.GetGroupBy() +} + +func (u *UnionSelect) OrderBy(fields ...sql.SQLObject) sql.ISelect { + u.MainSelect.OrderBy(fields...) + return u +} + +func (u *UnionSelect) GetOrderBy() []sql.SQLObject { + return u.MainSelect.GetOrderBy() +} + +func (u *UnionSelect) Limit(limit sql.SQLObject) sql.ISelect { + u.MainSelect.Limit(limit) + return u +} + +func (u *UnionSelect) GetLimit() sql.SQLObject { + return u.MainSelect.GetLimit() +} + +func (u *UnionSelect) Offset(offset sql.SQLObject) sql.ISelect { + u.MainSelect.Offset(offset) + return u +} + +func (u *UnionSelect) GetOffset() sql.SQLObject { + return u.MainSelect.GetOffset() +} + +func (u *UnionSelect) With(withs ...*sql.With) sql.ISelect { + u.MainSelect.With(withs...) + return u +} + +func (u *UnionSelect) AddWith(withs ...*sql.With) sql.ISelect { + u.MainSelect.AddWith(withs...) + return u +} + +func (u *UnionSelect) DropWith(alias ...string) sql.ISelect { + u.MainSelect.DropWith(alias...) + return u +} + +func (u *UnionSelect) GetWith() []*sql.With { + var w []*sql.With = u.MainSelect.GetWith() + for _, ww := range u.SubSelects { + w = append(w, ww.GetWith()...) + } + return w +} + +func (u *UnionSelect) Join(joins ...*sql.Join) sql.ISelect { + u.MainSelect.Join(joins...) + return u +} + +func (u *UnionSelect) AddJoin(joins ...*sql.Join) sql.ISelect { + u.MainSelect.AddJoin(joins...) + return u +} + +func (u *UnionSelect) GetJoin() []*sql.Join { + return u.MainSelect.GetJoin() +} + +func (u *UnionSelect) String(ctx *sql.Ctx, options ...int) (string, error) { + return u.MainSelect.String(ctx, options...) +} + +func (u *UnionSelect) SetSetting(name string, value string) sql.ISelect { + u.MainSelect.SetSetting(name, value) + return u +} + +func (u *UnionSelect) GetSettings(table sql.SQLObject) map[string]string { + return u.MainSelect.GetSettings(table) +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_multi_stream_select.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_multi_stream_select.go new file mode 100644 index 00000000..395adce0 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_multi_stream_select.go @@ -0,0 +1,25 @@ +package clickhouse_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type MultiStreamSelectPlanner struct { + Mains []shared.SQLRequestPlanner +} + +func (m *MultiStreamSelectPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + if len(m.Mains) == 1 { + return m.Mains[0].Process(ctx) + } + var err error + selects := make([]sql.ISelect, len(m.Mains)) + for i, main := range m.Mains { + selects[i], err = main.Process(ctx) + if err != nil { + return nil, err + } + } + return &UnionAll{selects[0], selects[1:]}, nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_parser.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_parser.go new file mode 100644 index 00000000..f382e9be --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_parser.go @@ -0,0 +1,41 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type ParserPlanner struct { + Op string + labels []string + Vals []string + Main shared.SQLRequestPlanner +} + +func (p *ParserPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + var ( + req sql.ISelect + err error + ) + switch p.Op { + case "regexp": + req, err = p.regexp(ctx) + case "json": + req, err = p.json(ctx) + default: + return nil, &shared.NotSupportedError{fmt.Sprintf("%s not supported", p.Op)} + } + if err != nil { + return nil, err + } + + sel, err := patchCol(req.GetSelect(), "fingerprint", func(object sql.SQLObject) (sql.SQLObject, error) { + return sql.NewRawObject(`cityHash64(arraySort(arrayZip(mapKeys(labels),mapValues(labels))))`), nil + }) + if err != nil { + return nil, err + } + + return req.Select(sel...), nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_parser_json.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_parser_json.go new file mode 100644 index 00000000..9e953b1a --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_parser_json.go @@ -0,0 +1,84 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "strings" +) + +func (p *ParserPlanner) json(ctx *shared.PlannerContext) (sql.ISelect, error) { + req, err := p.Main.Process(ctx) + if err != nil { + return nil, err + } + + jsonPaths := make([][]string, len(p.Vals)) + for i, val := range p.Vals { + jsonPaths[i], err = shared.JsonPathParamToArray(val) + if err != nil { + return nil, err + } + } + + sel, err := patchCol(req.GetSelect(), "labels", func(object sql.SQLObject) (sql.SQLObject, error) { + return &sqlMapUpdate{ + object, + &sqlJsonParser{ + col: sql.NewRawObject("string"), + labels: p.labels, + paths: jsonPaths, + }, + }, nil + }) + + return req.Select(sel...), nil +} + +type sqlJsonParser struct { + col sql.SQLObject + labels []string + paths [][]string +} + +func (s *sqlJsonParser) String(ctx *sql.Ctx, opts ...int) (string, error) { + strLabels := make([]string, len(s.labels)) + strVals := make([]string, len(s.labels)) + for i, l := range s.labels { + var err error + strLabels[i], err = (sql.NewStringVal(l)).String(ctx, opts...) + if err != nil { + return "", err + } + + strVals[i], err = s.path2Sql(s.paths[i], ctx, opts...) + if err != nil { + return "", err + } + } + return fmt.Sprintf("mapFromArrays([%s], [%s])", + strings.Join(strLabels, ","), + strings.Join(strVals, ",")), nil +} + +func (s *sqlJsonParser) path2Sql(path []string, ctx *sql.Ctx, opts ...int) (string, error) { + colName, err := s.col.String(ctx, opts...) + if err != nil { + return "", err + } + + res := make([]string, len(path)) + for i, part := range path { + var err error + res[i], err = (sql.NewStringVal(part)).String(ctx, opts...) + if err != nil { + return "", err + } + } + partId := fmt.Sprintf("jp_%d", ctx.Id()) + + return fmt.Sprintf(`if(JSONType(%[3]s, %[1]s as %[2]s) == 'String', `+ + `JSONExtractString(%[3]s, %[2]s), `+ + `JSONExtractRaw(%[3]s, %[2]s)`+ + `)`, strings.Join(res, ","), partId, colName), nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_parser_regexp.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_parser_regexp.go new file mode 100644 index 00000000..59f599c4 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_parser_regexp.go @@ -0,0 +1,156 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/alecthomas/participle/v2" + "github.com/alecthomas/participle/v2/lexer" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "strings" +) + +func (p *ParserPlanner) regexp(ctx *shared.PlannerContext) (sql.ISelect, error) { + req, err := p.Main.Process(ctx) + if err != nil { + return nil, err + } + + ast, err := p.parseRe(p.Vals[0]) + if err != nil { + return nil, err + } + + names := ast.collectGroupNames(nil) + + sel, err := patchCol(req.GetSelect(), "labels", func(object sql.SQLObject) (sql.SQLObject, error) { + return &sqlMapUpdate{ + m1: object, + m2: ®exMap{ + col: sql.NewRawObject("string"), + labels: names, + re: ast.String(), + }, + }, nil + }) + if err != nil { + return nil, err + } + + return req.Select(sel...), nil +} + +func (p *ParserPlanner) parseRe(re string) (*regexAST, error) { + parser, err := participle.Build[regexAST](participle.Lexer(regexParserDesc)) + if err != nil { + return nil, err + } + res, err := parser.ParseString("", re) + return res, err +} + +var regexParserDesc = lexer.MustSimple([]lexer.SimpleRule{ + {"OBrackQ", "\\(\\?P<"}, + {"OBrack", "\\("}, + {"CBrack", "\\)"}, + {"CCBrack", ">"}, + {"Ident", "[a-zA-Z_][0-9a-zA-Z_]*"}, + {"Char", `\\.|.`}, +}) + +type regexAST struct { + RegexPart []regexPart `@@+` +} + +func (r *regexAST) String() string { + res := make([]string, len(r.RegexPart)) + for i, r := range r.RegexPart { + res[i] = r.String() + } + return strings.Join(res, "") +} +func (r *regexAST) collectGroupNames(init []string) []string { + for _, p := range r.RegexPart { + init = p.collectGroupNames(init) + } + return init +} + +type regexPart struct { + SimplePart string `@(Char|CCBrack|Ident)+` + NamedBrackPart *brackPart `| OBrackQ @@ CBrack` + BrackPart *regexAST `| OBrack @@ CBrack` +} + +func (r *regexPart) String() string { + if r.SimplePart != "" { + return r.SimplePart + } + if r.NamedBrackPart != nil { + return "(" + r.NamedBrackPart.String() + ")" + } + return "(" + r.BrackPart.String() + ")" +} +func (r *regexPart) collectGroupNames(init []string) []string { + if r.NamedBrackPart != nil { + return r.NamedBrackPart.collectGroupNames(init) + } + if r.BrackPart != nil { + init = append(init, "") + return r.BrackPart.collectGroupNames(init) + } + return init +} + +type brackPart struct { + Name string `@Ident CCBrack` + Tail *regexAST `@@?` +} + +func (b *brackPart) String() string { + return b.Tail.String() +} +func (b *brackPart) collectGroupNames(init []string) []string { + init = append(init, b.Name) + init = b.Tail.collectGroupNames(init) + return init +} + +type regexMap struct { + col sql.SQLObject + labels []string + re string +} + +func (r *regexMap) String(ctx *sql.Ctx, opts ...int) (string, error) { + strCol, err := r.col.String(ctx, opts...) + if err != nil { + return "", err + } + + strLabels := make([]string, len(r.labels)) + for i, l := range r.labels { + var err error + strLabels[i], err = (sql.NewStringVal(l)).String(ctx, opts...) + if err != nil { + return "", err + } + } + + strRe, err := (sql.NewStringVal(r.re)).String(ctx, opts...) + if err != nil { + return "", err + } + + id := ctx.Id() + + return fmt.Sprintf("mapFromArrays("+ + "arrayFilter("+ + " (x,y) -> x != '' AND y != '',"+ + " [%[1]s] as re_lbls_%[2]d,"+ + " arrayMap(x -> x[length(x)], extractAllGroupsHorizontal(%[4]s, %[3]s)) as re_vals_%[2]d),"+ + "arrayFilter((x,y) -> x != '' AND y != '', re_vals_%[2]d, re_lbls_%[2]d))", + strings.Join(strLabels, ","), + id, + strRe, + strCol), nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_plan_fingerprints.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_plan_fingerprints.go new file mode 100644 index 00000000..9b25371c --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_plan_fingerprints.go @@ -0,0 +1,22 @@ +package clickhouse_planner + +import "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + +func (p *planner) planFingerprints() (shared.SQLRequestPlanner, error) { + var ( + labelNames []string + ops []string + values []string + ) + for _, label := range p.script.StrSelector.StrSelCmds { + labelNames = append(labelNames, label.Label.Name) + ops = append(ops, label.Op) + val, err := label.Val.Unquote() + if err != nil { + return nil, err + } + values = append(values, val) + } + res := NewStreamSelectPlanner(labelNames, ops, values) + return res, nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_quantile.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_quantile.go new file mode 100644 index 00000000..3f677fb7 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_quantile.go @@ -0,0 +1,41 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "time" +) + +type QuantilePlanner struct { + Main shared.SQLRequestPlanner + Param float64 + Duration time.Duration +} + +func (p *QuantilePlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := p.Main.Process(ctx) + if err != nil { + return nil, err + } + + quantA := sql.NewWith(main, "quant_a") + + hasLabels := hasColumn(main.GetSelect(), "labels") + + res := sql.NewSelect(). + With(quantA). + Select( + sql.NewSimpleCol("quant_a.fingerprint", "fingerprint"), + sql.NewSimpleCol(fmt.Sprintf("intDiv(quant_a.timestamp_ns, %d) * %[1]d", + p.Duration.Nanoseconds()), "timestamp_ns"), + sql.NewSimpleCol(fmt.Sprintf("quantile(%f)(value)", p.Param), "value")). + From(sql.NewWithRef(quantA)). + GroupBy(sql.NewRawObject("timestamp_ns"), sql.NewRawObject("fingerprint")) + + if hasLabels { + res.Select(append(res.GetSelect(), sql.NewSimpleCol("any(quant_a.labels)", "labels"))...) + } + + return res, nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_series.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_series.go new file mode 100644 index 00000000..0edf4f8e --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_series.go @@ -0,0 +1,43 @@ +package clickhouse_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/plugins" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type SeriesPlanner struct { + FingerprintsPlanner shared.SQLRequestPlanner +} + +func NewSeriesPlanner(fingerprintsPlanner shared.SQLRequestPlanner) shared.SQLRequestPlanner { + p := plugins.GetSeriesPlannerPlugin() + if p != nil { + return (*p)(fingerprintsPlanner) + } + return &SeriesPlanner{FingerprintsPlanner: fingerprintsPlanner} +} + +func (s *SeriesPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + fpSel, err := s.FingerprintsPlanner.Process(ctx) + if err != nil { + return nil, err + } + withFPSel := sql.NewWith(fpSel, "fp_sel") + tableName := ctx.TimeSeriesTableName + if ctx.IsCluster { + tableName = ctx.TimeSeriesDistTableName + } + req := sql.NewSelect().With(withFPSel).Distinct(true). + Select(sql.NewSimpleCol("labels", "labels")). + From(sql.NewSimpleCol(tableName, "time_series")). + AndWhere( + sql.Ge(sql.NewRawObject("date"), sql.NewStringVal(FormatFromDate(ctx.From))), + sql.Le(sql.NewRawObject("date"), sql.NewStringVal(ctx.To.Format("2006-01-02"))), + sql.NewIn(sql.NewRawObject("fingerprint"), sql.NewWithRef(withFPSel)), + GetTypes(ctx)) + if ctx.Limit > 0 { + req.Limit(sql.NewIntVal(ctx.Limit)) + } + return req, nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_simple_label_filter.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_simple_label_filter.go new file mode 100644 index 00000000..872a077e --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_simple_label_filter.go @@ -0,0 +1,37 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_parser" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +// Filter labels on time_series table if no parsers are +// applied before +type SimpleLabelFilterPlanner struct { + Expr *logql_parser.LabelFilter + FPSel shared.SQLRequestPlanner +} + +func (s *SimpleLabelFilterPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := s.FPSel.Process(ctx) + if err != nil { + return nil, err + } + + id := fmt.Sprintf("subsel_%d", ctx.Id()) + withMain := sql.NewWith(main, id) + filterPlanner := &LabelFilterPlanner{ + Expr: s.Expr, + MainReq: sql.NewSelect(). + With(withMain). + Select(sql.NewRawObject("fingerprint")). + From(sql.NewRawObject(ctx.TimeSeriesTableName)). + AndWhere(sql.NewIn(sql.NewRawObject("fingerprint"), sql.NewWithRef(withMain))), + LabelValGetter: func(s string) sql.SQLObject { + return sql.NewRawObject(fmt.Sprintf("JSONExtractString(labels, '%s')", s)) + }, + } + return filterPlanner.Process(ctx) +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_step_fix.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_step_fix.go new file mode 100644 index 00000000..b6d0d7cb --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_step_fix.go @@ -0,0 +1,41 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "time" +) + +type StepFixPlanner struct { + Main shared.SQLRequestPlanner + Duration time.Duration +} + +func (s *StepFixPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := s.Main.Process(ctx) + if err != nil { + return nil, err + } + + if s.Duration.Nanoseconds() >= ctx.Step.Nanoseconds() { + return main, nil + } + + witMain := sql.NewWith(main, "pre_step_fix") + res := sql.NewSelect().With(witMain). + Select( + sql.NewSimpleCol( + fmt.Sprintf("intDiv(pre_step_fix.timestamp_ns, %d) * %[1]d", ctx.Step.Nanoseconds()), + "timestamp_ns"), + sql.NewRawObject("fingerprint"), + sql.NewSimpleCol(`''`, "string"), + sql.NewSimpleCol("argMin(pre_step_fix.value, pre_step_fix.timestamp_ns)", "value"), + ).From(sql.NewWithRef(witMain)). + GroupBy(sql.NewRawObject("timestamp_ns"), sql.NewRawObject("fingerprint")) + + if hasColumn(main.GetSelect(), "labels") { + res.Select(append(res.GetSelect(), sql.NewSimpleCol("any(labels)", "labels"))...) + } + return res, nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_stream_select.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_stream_select.go new file mode 100644 index 00000000..19b1dfb5 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_stream_select.go @@ -0,0 +1,88 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/plugins" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "strings" +) + +type StreamSelectPlanner struct { + LabelNames []string + Ops []string + Values []string +} + +func NewStreamSelectPlanner(labelNames, ops, values []string) shared.SQLRequestPlanner { + p := plugins.GetStreamSelectPlannerPlugin() + if p != nil { + return (*p)(labelNames, ops, values) + } + return &StreamSelectPlanner{ + LabelNames: labelNames, + Ops: ops, + Values: values, + } +} + +func (s *StreamSelectPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + clauses := make([]sql.SQLCondition, len(s.LabelNames)) + for i, name := range s.LabelNames { + var valClause sql.SQLCondition + switch s.Ops[i] { + case "=": + valClause = sql.Eq(sql.NewRawObject("val"), sql.NewStringVal(s.Values[i])) + break + case "!=": + valClause = sql.Neq(sql.NewRawObject("val"), sql.NewStringVal(s.Values[i])) + break + case "=~": + valClause = sql.Eq(&sqlMatch{ + col: sql.NewRawObject("val"), pattern: s.Values[i]}, sql.NewIntVal(1)) + break + case "!~": + valClause = sql.Eq(&sqlMatch{ + col: sql.NewRawObject("val"), pattern: s.Values[i]}, sql.NewIntVal(0)) + break + default: + return nil, &shared.NotSupportedError{ + fmt.Sprintf("%s op not supported", s.Ops[i])} + } + clauses[i] = sql.And( + sql.Eq(sql.NewRawObject("key"), sql.NewStringVal(name)), + valClause) + } + + fpRequest := sql.NewSelect(). + Select(sql.NewRawObject("fingerprint")). + From(sql.NewRawObject(ctx.TimeSeriesGinTableName)). + AndWhere( + sql.Ge(sql.NewRawObject("date"), sql.NewStringVal(FormatFromDate(ctx.From))), + GetTypes(ctx), + sql.Or(clauses...)). + GroupBy(sql.NewRawObject("fingerprint")). + AndHaving(sql.Eq(&SqlBitSetAnd{clauses}, sql.NewIntVal((1<<len(clauses))-1))) + return fpRequest, nil +} + +type SqlBitSetAnd struct { + clauses []sql.SQLCondition +} + +func NewSqlBitSetAnd(clauses []sql.SQLCondition) *SqlBitSetAnd { + return &SqlBitSetAnd{clauses: clauses} +} + +func (s *SqlBitSetAnd) String(ctx *sql.Ctx, options ...int) (string, error) { + strConditions := make([]string, len(s.clauses)) + for i, c := range s.clauses { + var err error + strConditions[i], err = c.String(ctx, options...) + if err != nil { + return "", err + } + strConditions[i] = fmt.Sprintf("bitShiftLeft(%s, %d)", strConditions[i], i) + } + return fmt.Sprintf("groupBitOr(%s)", strings.Join(strConditions, " + ")), nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_test.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_test.go new file mode 100644 index 00000000..0996b8a5 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_test.go @@ -0,0 +1,14 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_parser" + "testing" +) + +func TestPlanner(t *testing.T) { + script := "sum(sum_over_time({test_id=\"${testID}_json\"}| json | unwrap str_id [10s]) by (test_id, str_id)) by (test_id) > 100" + ast, _ := logql_parser.Parse(script) + fmt.Println(findFirst[logql_parser.StrSelCmd](ast)) + +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_time_series_init.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_time_series_init.go new file mode 100644 index 00000000..f281ccae --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_time_series_init.go @@ -0,0 +1,32 @@ +package clickhouse_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/plugins" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type TimeSeriesInitPlanner struct { +} + +func NewTimeSeriesInitPlanner() shared.SQLRequestPlanner { + p := plugins.GetTimeSeriesInitPlannerPlugin() + if p != nil { + return (*p)() + } + return &TimeSeriesInitPlanner{} +} + +func (t *TimeSeriesInitPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + return sql.NewSelect(). + Select( + sql.NewSimpleCol("time_series.fingerprint", "fingerprint"), + sql.NewSimpleCol("mapFromArrays("+ + "arrayMap(x -> x.1, JSONExtractKeysAndValues(time_series.labels, 'String') as rawlbls), "+ + "arrayMap(x -> x.2, rawlbls))", "labels")). + From(sql.NewSimpleCol(ctx.TimeSeriesDistTableName, "time_series")). + AndPreWhere( + sql.Ge(sql.NewRawObject("time_series.date"), sql.NewStringVal(FormatFromDate(ctx.From))), + GetTypes(ctx), + ), nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_topk.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_topk.go new file mode 100644 index 00000000..f8b91f2b --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_topk.go @@ -0,0 +1,71 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type TopKPlanner struct { + Main shared.SQLRequestPlanner + Len int + IsTop bool +} + +func (t *TopKPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := t.Main.Process(ctx) + if err != nil { + return nil, err + } + + hasLabels := hasColumn(main.GetSelect(), "labels") + + withParA := sql.NewWith(main, "par_a") + lambda := "" + if t.IsTop { + lambda = "x -> (-x.1, x.2" + if hasLabels { + lambda += ", x.3" + } + lambda += ")," + } + if err != nil { + return nil, err + } + + tuple := "par_a.value, par_a.fingerprint" + if hasLabels { + tuple += ", par_a.labels" + } + + q := sql.NewSelect(). + With(withParA). + Select( + sql.NewSimpleCol("par_a.timestamp_ns", "timestamp_ns"), + sql.NewSimpleCol( + fmt.Sprintf( + "arraySlice(arraySort(%sgroupArray((%s))), 1, %d)", + lambda, + tuple, + t.Len), + "slice")). + From(sql.NewWithRef(withParA)). + GroupBy(sql.NewRawObject("timestamp_ns")) + + withParB := sql.NewWith(q, "par_b") + + q = sql.NewSelect(). + With(withParB). + Select( + sql.NewSimpleCol("arr_b.2", "fingerprint"), + sql.NewSimpleCol("par_b.timestamp_ns", "timestamp_ns"), + sql.NewSimpleCol("arr_b.1", "value"), + sql.NewSimpleCol("''", "string")). + From(sql.NewWithRef(withParB)). + Join(sql.NewJoin("array", sql.NewSimpleCol("par_b.slice", "arr_b"), nil)) + + if hasLabels { + q.Select(append(q.GetSelect(), sql.NewSimpleCol("arr_b.3", "labels"))...) + } + return q, nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_unwrap.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_unwrap.go new file mode 100644 index 00000000..16ee02ec --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_unwrap.go @@ -0,0 +1,101 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type UnwrapPlanner struct { + Main shared.SQLRequestPlanner + Label string + UseTimeSeriesTable bool + labelsCache **sql.With + fpCache **sql.With +} + +func (u *UnwrapPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + mainReq, err := u.Main.Process(ctx) + if err != nil { + return nil, err + } + if u.UseTimeSeriesTable { + return u.processTimeSeries(ctx, mainReq) + } else { + return u.processSimple(ctx, mainReq) + } +} + +func (u *UnwrapPlanner) processSimple(ctx *shared.PlannerContext, main sql.ISelect) (sql.ISelect, error) { + sel := main.GetSelect() + labels := getCol(main, "labels") + strCol := getCol(main, "string") + if labels == nil { + return nil, fmt.Errorf("labels col not inited") + } + label := u.Label + + sel, err := patchCol(sel, "value", func(object sql.SQLObject) (sql.SQLObject, error) { + return sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + var strLabel string + if u.Label != "_entry" { + strLabels, err := labels.String(ctx, options...) + if err != nil { + return "", err + } + val, err := sql.NewStringVal(label).String(ctx, options...) + if err != nil { + return "", err + } + strLabel = fmt.Sprintf("%s[%s]", strLabels, val) + } else { + var err error + strLabel, err = strCol.String(ctx, options...) + if err != nil { + return "", err + } + } + return fmt.Sprintf("toFloat64OrZero(%s)", strLabel), nil + }), nil + }) + if err != nil { + return nil, err + } + + return main.Select(sel...), nil +} + +func (u *UnwrapPlanner) processTimeSeries(ctx *shared.PlannerContext, main sql.ISelect) (sql.ISelect, error) { + var from sql.SQLObject + if *u.labelsCache != nil { + from = sql.NewWithRef(*u.labelsCache) + } else { + var err error + from, err = labelsFromScratch(ctx, *u.fpCache) + if err != nil { + return nil, err + } + } + + subSelect := sql.NewSelect().Select( + sql.NewRawObject("fingerprint"), + sql.NewRawObject("labels"), + ).From(from) + + subSelect, err := u.processSimple(ctx, main) + if err != nil { + return nil, err + } + + joinType := "ANY LEFT " + if ctx.IsCluster { + joinType = "GLOBAL ANY LEFT " + } + + main.Select(append(main.GetSelect(), + sql.NewSimpleCol("sub_value.value", "value"), + sql.NewSimpleCol("sub_value.labels", "labels"))...). + Join(sql.NewJoin(joinType, sql.NewCol(subSelect, "sub_value"), + sql.Eq(sql.NewRawObject("fingerprint"), sql.NewRawObject("sub_value.fingerprint")))) + return main, nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_unwrap_function.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_unwrap_function.go new file mode 100644 index 00000000..c314c780 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_unwrap_function.go @@ -0,0 +1,60 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "time" +) + +type UnwrapFunctionPlanner struct { + Main shared.SQLRequestPlanner + Func string + Duration time.Duration + WithLabels bool +} + +func (u *UnwrapFunctionPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := u.Main.Process(ctx) + if err != nil { + return nil, err + } + + withMain := sql.NewWith(main, "unwrap_1") + + var val sql.SQLObject + switch u.Func { + case "rate": + val = sql.NewRawObject(fmt.Sprintf("sum(unwrap_1.value) / %f", + float64(u.Duration.Milliseconds())/1000)) + case "sum_over_time": + val = sql.NewRawObject("sum(unwrap_1.value)") + case "avg_over_time": + val = sql.NewRawObject("avg(unwrap_1.value)") + case "max_over_time": + val = sql.NewRawObject("max(unwrap_1.value)") + case "min_over_time": + val = sql.NewRawObject("min(unwrap_1.value)") + case "first_over_time": + val = sql.NewRawObject("argMin(unwrap_1.value, unwrap_1.timestamp_ns)") + case "last_over_time": + val = sql.NewRawObject("argMax(unwrap_1.value, unwrap_1.timestamp_ns)") + case "stdvar_over_time": + val = sql.NewRawObject("varPop(unwrap_1.value)") + case "stddev_over_time": + val = sql.NewRawObject("stddevPop(unwrap_1.value)") + } + + res := sql.NewSelect().With(withMain).Select( + sql.NewSimpleCol( + fmt.Sprintf("intDiv(timestamp_ns, %d) * %[1]d", u.Duration.Nanoseconds()), + "timestamp_ns"), + sql.NewRawObject("fingerprint"), + sql.NewSimpleCol(`''`, "string"), + sql.NewCol(val, "value"), + sql.NewSimpleCol("any(labels)", "labels")). + From(sql.NewWithRef(withMain)). + GroupBy(sql.NewRawObject("fingerprint"), sql.NewRawObject("timestamp_ns")) + + return res, nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_values.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_values.go new file mode 100644 index 00000000..2604940a --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_values.go @@ -0,0 +1,46 @@ +package clickhouse_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/plugins" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type ValuesPlanner struct { + FingerprintsPlanner shared.SQLRequestPlanner + Key string +} + +func NewValuesPlanner(fingerprintsPlanner shared.SQLRequestPlanner, key string) shared.SQLRequestPlanner { + p := plugins.GetValuesPlannerPlugin() + if p != nil { + return (*p)(fingerprintsPlanner, key) + } + return &ValuesPlanner{FingerprintsPlanner: fingerprintsPlanner, Key: key} +} + +func (v *ValuesPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + res := sql.NewSelect().Select(sql.NewRawObject("val")).Distinct(true). + From(sql.NewRawObject(ctx.TimeSeriesGinTableName)). + AndWhere( + sql.Ge(sql.NewRawObject("date"), sql.NewStringVal(FormatFromDate(ctx.From))), + sql.Le(sql.NewRawObject("date"), sql.NewStringVal(ctx.To.Format("2006-01-02"))), + sql.Eq(sql.NewRawObject("key"), sql.NewStringVal(v.Key)), + GetTypes(ctx), + ) + if v.FingerprintsPlanner != nil { + fp, err := v.FingerprintsPlanner.Process(ctx) + if err != nil { + return nil, err + } + + withFp := sql.NewWith(fp, "fp_sel") + res = res.With(withFp). + AndWhere(sql.NewIn(sql.NewRawObject("fingerprint"), sql.NewWithRef(withFp))) + } + + if ctx.Limit > 0 { + res.Limit(sql.NewIntVal(ctx.Limit)) + } + return res, nil +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_with_connector.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_with_connector.go new file mode 100644 index 00000000..e7b8ed4e --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/planner_with_connector.go @@ -0,0 +1,36 @@ +package clickhouse_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type WithConnectorPlanner struct { + Main shared.SQLRequestPlanner + With shared.SQLRequestPlanner + Alias string + + ProcessFn func(q sql.ISelect, w *sql.With) (sql.ISelect, error) + + WithCache **sql.With +} + +func (w *WithConnectorPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := w.Main.Process(ctx) + if err != nil { + return nil, err + } + var with *sql.With + if w.WithCache != nil && *w.WithCache != nil { + with = *w.WithCache + } else { + withReq, err := w.With.Process(ctx) + if err != nil { + return nil, err + } + with = sql.NewWith(withReq, w.Alias) + *w.WithCache = with + } + + return w.ProcessFn(main.With(with), with) +} diff --git a/reader/logql/logql_transpiler_v2/clickhouse_planner/sql_misc.go b/reader/logql/logql_transpiler_v2/clickhouse_planner/sql_misc.go new file mode 100644 index 00000000..e5b6127c --- /dev/null +++ b/reader/logql/logql_transpiler_v2/clickhouse_planner/sql_misc.go @@ -0,0 +1,183 @@ +package clickhouse_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "strings" + "time" +) + +type sqlMatch struct { + col sql.SQLObject + pattern string + patternObj sql.SQLObject +} + +func (s *sqlMatch) String(ctx *sql.Ctx, opts ...int) (string, error) { + strCol, err := s.col.String(ctx, opts...) + if err != nil { + return "", err + } + + if s.patternObj == nil { + s.patternObj = sql.NewStringVal(s.pattern) + } + + strVal, err := s.patternObj.String(ctx, opts...) + if err != nil { + return "", err + } + + return fmt.Sprintf("match(%s, %s)", strCol, strVal), nil +} + +type sqlMapUpdate struct { + m1 sql.SQLObject + m2 sql.SQLObject +} + +func (s *sqlMapUpdate) String(ctx *sql.Ctx, opts ...int) (string, error) { + str1, err := s.m1.String(ctx, opts...) + if err != nil { + return "", err + } + str2, err := s.m2.String(ctx, opts...) + if err != nil { + return "", err + } + return fmt.Sprintf("mapUpdate(%s, %s)", str1, str2), nil +} + +func patchCol(cols []sql.SQLObject, name string, + patch func(sql.SQLObject) (sql.SQLObject, error)) ([]sql.SQLObject, error) { + _cols := make([]sql.SQLObject, len(cols)) + for i, c := range cols { + _c, ok := c.(sql.Aliased) + if !ok || _c.GetAlias() != name { + _cols[i] = c + continue + } + + __c, err := patch(_c.GetExpr()) + if err != nil { + return nil, err + } + _cols[i] = sql.NewCol(__c, name) + } + return _cols, nil +} + +func hasColumn(cols []sql.SQLObject, name string) bool { + for _, c := range cols { + if _c, ok := c.(sql.Aliased); ok && _c.GetAlias() == name { + return true + } + } + return false +} + +type sqlMapInit struct { + TypeName string + Keys []sql.SQLObject + Values []sql.SQLObject +} + +func (m *sqlMapInit) String(ctx *sql.Ctx, opts ...int) (string, error) { + str := [][]string{ + make([]string, len(m.Keys)), + make([]string, len(m.Values)), + } + for j, objs := range [][]sql.SQLObject{m.Keys, m.Values} { + for i, k := range objs { + var err error + str[j][i], err = k.String(ctx, opts...) + if err != nil { + return "", err + } + } + } + + return fmt.Sprintf("([%s],[%s])::%s", + strings.Join(str[0], ","), + strings.Join(str[1], ","), + m.TypeName), nil +} + +type sqlFormat struct { + format string + args []sql.SQLObject +} + +func (s *sqlFormat) String(ctx *sql.Ctx, opts ...int) (string, error) { + args := make([]string, len(s.args)) + for i, a := range s.args { + var err error + args[i], err = a.String(ctx, opts...) + if err != nil { + return "", err + } + } + + format, err := sql.NewStringVal(s.format).String(ctx, opts...) + if err != nil { + return "", err + } + + return fmt.Sprintf("format(%s, %s)", format, strings.Join(args, ", ")), nil +} + +func getCol(req sql.ISelect, alias string) sql.SQLObject { + cols := req.GetSelect() + for _, c := range cols { + if a, ok := c.(sql.Aliased); ok && a.GetAlias() == alias { + return a.GetExpr() + } + } + return nil +} + +func labelsFromScratch(ctx *shared.PlannerContext, fpCache *sql.With) (sql.ISelect, error) { + _from, err := NewTimeSeriesInitPlanner().Process(ctx) + if err != nil { + return nil, err + } + _from.AndPreWhere(sql.NewIn(sql.NewRawObject("time_series.fingerprint"), sql.NewWithRef(fpCache))) + return _from, nil +} + +func GetTypes(ctx *shared.PlannerContext) *sql.In { + tp := ctx.Type + if tp == shared.SAMPLES_TYPE_BOTH { + tp = shared.SAMPLES_TYPE_LOGS + } + return sql.NewIn(sql.NewRawObject("type"), sql.NewIntVal(int64(tp)), + sql.NewIntVal(shared.SAMPLES_TYPE_BOTH)) +} + +type UnionAll struct { + sql.ISelect + Anothers []sql.ISelect +} + +func (u *UnionAll) String(ctx *sql.Ctx, opts ...int) (string, error) { + selects := make([]string, len(u.Anothers)+1) + var err error + selects[0], err = u.ISelect.String(ctx, opts...) + if err != nil { + return "", err + } + + for i, s := range u.Anothers { + selects[i+1], err = s.String(ctx, opts...) + if err != nil { + return "", err + } + } + + return strings.Join(selects, " UNION ALL "), nil +} + +func FormatFromDate(from time.Time) string { + return from.UTC().Add(time.Minute * -30).Format("2006-01-02") +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/hash.go b/reader/logql/logql_transpiler_v2/internal_planner/hash.go new file mode 100644 index 00000000..eda9b6d9 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/hash.go @@ -0,0 +1,27 @@ +package internal_planner + +import ( + "github.com/go-faster/city" + "unsafe" +) + +func fingerprint(labels map[string]string) uint64 { + descr := [3]uint64{0, 0, 1} + for k, v := range labels { + a := k + v + descr[0] += city.CH64([]byte(a)) + descr[1] ^= city.CH64([]byte(a)) + descr[2] *= 1779033703 + 2*city.CH64([]byte(a)) + + } + return city.CH64(unsafe.Slice((*byte)(unsafe.Pointer(&descr[0])), 24)) +} + +func contains(slice []string, s string) bool { + for _, v := range slice { + if v == s { + return true + } + } + return false +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/hash_test.go b/reader/logql/logql_transpiler_v2/internal_planner/hash_test.go new file mode 100644 index 00000000..3a11791e --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/hash_test.go @@ -0,0 +1,155 @@ +package internal_planner + +import ( + "fmt" + "github.com/go-faster/city" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "testing" + "unsafe" +) + +func BenchmarkCH(b *testing.B) { + labels := map[string]string{ + "a": "b", + "b": "b", + "c": "b", + "d": "b", + } + for i := 0; i < b.N; i++ { + city.CH64([]byte(fmt.Sprintf("%v", labels))) + } +} + +func BenchmarkCH2(b *testing.B) { + labels := map[string]string{ + "a": "b", + "b": "b", + "c": "b", + "d": "b", + } + for i := 0; i < b.N; i++ { + descr := [3]uint64{0, 0, 1} + for k, v := range labels { + a := k + v + descr[0] += city.CH64([]byte(a)) + descr[1] ^= city.CH64([]byte(a)) + descr[2] *= 1779033703 + 2*city.CH64([]byte(a)) + + } + city.CH64(unsafe.Slice((*byte)(unsafe.Pointer(&descr[0])), 24)) + } +} + +func BenchmarkDelete(b *testing.B) { + for i := 0; i < b.N; i++ { + labels := map[string]string{ + "a": "b", + "b": "b", + "c": "b", + "d": "b", + } + for k := range labels { + if k != "a" { + delete(labels, k) + } + } + } +} + +func BenchmarkDelete2(b *testing.B) { + for i := 0; i < b.N; i++ { + labels := map[string]string{ + "a": "b", + "b": "b", + "c": "b", + "d": "b", + } + _labels := make(map[string]string) + for k, v := range labels { + if k == "a" { + _labels[k] = v + } + } + } +} + +func BenchmarkMap(b *testing.B) { + labels := map[string]string{ + "a": "b", + "b": "", + } + for i := 0; i < b.N; i++ { + _labels := map[string]string{ + "a": "b", + "b": "b", + "c": "a", + "d": "q", + "f": "rt", + "sda": "wrwer", + } + for k, v := range _labels { + if labels[k] == v || labels[k] == "" { + delete(_labels, k) + } + } + } +} + +func BenchmarkMap2(b *testing.B) { + labels := []string{"a", "b"} + values := []string{"b", ""} + for i := 0; i < b.N; i++ { + _labels := map[string]string{ + "a": "b", + "b": "b", + "c": "a", + "d": "q", + "f": "rt", + "sda": "wrwer", + } + for k, v := range _labels { + for i, l := range labels { + if k == l && (v == values[i] || values[i] == "") { + delete(_labels, k) + } + } + } + } +} + +type FakePlanner struct { + out chan []shared.LogEntry +} + +func (f FakePlanner) IsMatrix() bool { + return false +} + +func (f FakePlanner) Process(context *shared.PlannerContext, c chan []shared.LogEntry) (chan []shared.LogEntry, error) { + return f.out, nil +} + +func TestParser(t *testing.T) { + out := make(chan []shared.LogEntry) + p := ParserPlanner{ + GenericPlanner: GenericPlanner{Main: &FakePlanner{out}}, + Op: "logfmt", + ParameterNames: []string{"lbl"}, + ParameterValues: []string{"a"}, + } + in, err := p.Process(nil, nil) + if err != nil { + t.Fatal(err) + } + go func() { + out <- []shared.LogEntry{{ + TimestampNS: 1, + Fingerprint: 1, + Labels: map[string]string{"a": "b", "b": "b"}, + Message: `a=dgdfgdfgdgf`, + Value: 0, + Err: nil, + }} + }() + fmt.Println(<-in) +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner.go b/reader/logql/logql_transpiler_v2/internal_planner/planner.go new file mode 100644 index 00000000..723bb0ba --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner.go @@ -0,0 +1,219 @@ +package internal_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_parser" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "reflect" + "strconv" + "strings" + "time" +) + +func Plan(script *logql_parser.LogQLScript, + in shared.RequestProcessor) (shared.RequestProcessor, error) { + strSelector := shared.GetStrSelector(script) + for _, ppl := range strSelector.Pipelines { + if ppl.LineFilter != nil { + str, err := ppl.LineFilter.Val.Unquote() + if err != nil { + return nil, err + } + in = &LineFilterPlanner{ + GenericPlanner: GenericPlanner{in}, + Op: ppl.LineFilter.Fn, + Val: str, + re: nil, + } + continue + } + if ppl.LabelFormat != nil { + in = &LabelFormatPlanner{ + GenericPlanner: GenericPlanner{in}, + LabelFormat: ppl.LabelFormat, + } + continue + } + if ppl.LabelFilter != nil { + in = &LabelFilterPlanner{ + GenericPlanner: GenericPlanner{in}, + Filter: ppl.LabelFilter, + } + continue + } + if ppl.LineFormat != nil { + str, err := ppl.LineFormat.Val.Unquote() + if err != nil { + return nil, err + } + in = &LineFormatterPlanner{ + GenericPlanner: GenericPlanner{in}, + Template: str, + } + continue + } + if ppl.Unwrap != nil { + in = &UnwrapPlanner{ + GenericPlanner: GenericPlanner{in}, + Label: ppl.Unwrap.Label.Name, + } + continue + } + if ppl.Parser != nil { + names := make([]string, len(ppl.Parser.ParserParams)) + vals := make([]string, len(ppl.Parser.ParserParams)) + for i, param := range ppl.Parser.ParserParams { + var err error + names[i] = param.Label.Name + vals[i], err = param.Val.Unquote() + if err != nil { + return nil, err + } + } + in = &ParserPlanner{ + GenericPlanner: GenericPlanner{in}, + Op: ppl.Parser.Fn, + ParameterNames: names, + ParameterValues: vals, + } + continue + } + if ppl.Drop != nil { + names := make([]string, len(ppl.Drop.Params)) + vals := make([]string, len(ppl.Drop.Params)) + for i, param := range ppl.Drop.Params { + names[i] = param.Label.Name + var ( + err error + val string + ) + if param.Val != nil { + val, err = param.Val.Unquote() + if err != nil { + return nil, err + } + } + vals[i] = val + } + in = &DropPlanner{ + GenericPlanner: GenericPlanner{in}, + Labels: names, + Values: vals, + } + } + } + in, err := planAggregators(script, in) + if err != nil { + return nil, err + } + if !in.IsMatrix() { + in = &LimitPlanner{GenericPlanner{in}} + in = &ResponseOptimizerPlanner{GenericPlanner{in}} + } + return in, err +} + +func planAggregators(script any, init shared.RequestProcessor) (shared.RequestProcessor, error) { + dfs := func(node ...any) (shared.RequestProcessor, error) { + for _, n := range node { + if n != nil && !reflect.ValueOf(n).IsNil() { + return planAggregators(n, init) + } + } + return init, nil + } + maybeComparison := func(proc shared.RequestProcessor, + comp *logql_parser.Comparison) (shared.RequestProcessor, error) { + if comp == nil { + return proc, nil + } + fVal, err := strconv.ParseFloat(comp.Val, 64) + if err != nil { + return nil, err + } + return &ComparisonPlanner{ + GenericPlanner: GenericPlanner{proc}, + Op: comp.Fn, + Val: fVal, + }, nil + } + + switch script.(type) { + case *logql_parser.LogQLScript: + script := script.(*logql_parser.LogQLScript) + return dfs(script.TopK, script.AggOperator, script.LRAOrUnwrap, script.QuantileOverTime) + case *logql_parser.AggOperator: + script := script.(*logql_parser.AggOperator) + proc, err := dfs(&script.LRAOrUnwrap) + if err != nil { + return nil, err + } + duration, err := time.ParseDuration(script.LRAOrUnwrap.Time + script.LRAOrUnwrap.TimeUnit) + if err != nil { + return nil, err + } + proc = planByWithout(proc, script.ByOrWithoutPrefix, script.ByOrWithoutSuffix) + return maybeComparison(&AggOpPlanner{ + AggregatorPlanner: AggregatorPlanner{ + GenericPlanner: GenericPlanner{proc}, + Duration: duration, + }, + Func: script.Fn, + }, script.Comparison) + case *logql_parser.LRAOrUnwrap: + script := script.(*logql_parser.LRAOrUnwrap) + duration, err := time.ParseDuration(script.Time + script.TimeUnit) + if err != nil { + return nil, err + } + var p shared.RequestProcessor + if len(script.StrSel.Pipelines) > 0 && script.StrSel.Pipelines[len(script.StrSel.Pipelines)-1].Unwrap != nil { + init = planByWithout(init, script.ByOrWithoutPrefix, script.ByOrWithoutSuffix) + p = &UnwrapAggPlanner{ + AggregatorPlanner: AggregatorPlanner{ + GenericPlanner: GenericPlanner{init}, + Duration: duration, + }, + Function: script.Fn, + } + } else { + p = &LRAPlanner{ + AggregatorPlanner: AggregatorPlanner{ + GenericPlanner: GenericPlanner{init}, + Duration: duration, + }, + Func: script.Fn, + } + } + return maybeComparison(p, script.Comparison) + case *logql_parser.QuantileOverTime: + return nil, &shared.NotSupportedError{Msg: "quantile_over_time is not supported"} + case *logql_parser.TopK: + return nil, &shared.NotSupportedError{Msg: "topk is not supported for the current request"} + } + return init, nil +} + +func planByWithout(init shared.RequestProcessor, + byWithout ...*logql_parser.ByOrWithout) shared.RequestProcessor { + var _byWithout *logql_parser.ByOrWithout + for _, b := range byWithout { + if b != nil { + _byWithout = b + } + } + + if _byWithout == nil { + return init + } + + labels := make([]string, len(_byWithout.Labels)) + for i, l := range _byWithout.Labels { + labels[i] = l.Name + } + + return &ByWithoutPlanner{ + GenericPlanner: GenericPlanner{init}, + By: strings.ToLower(_byWithout.Fn) == "by", + Labels: labels, + } +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_absent_over_time.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_absent_over_time.go new file mode 100644 index 00000000..63e4ef87 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_absent_over_time.go @@ -0,0 +1,29 @@ +package internal_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" +) + +type AbsentOverTimePlanner struct { + AggregatorPlanner +} + +func (a *AbsentOverTimePlanner) Process(ctx *shared.PlannerContext, + in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + return a.process(ctx, in, aggregatorPlannerOps{ + addValue: func(ctx *shared.PlannerContext, entry *shared.LogEntry, stream *aggOpStream) { + idx := (entry.TimestampNS - ctx.From.UnixNano()) / a.Duration.Nanoseconds() * 2 + if idx > 0 && idx < int64(len(stream.values)) { + stream.values[idx] = 0 + stream.values[idx+1] = 0 + } + }, + finalize: func(ctx *shared.PlannerContext, stream *aggOpStream) {}, + initStream: func(ctx *shared.PlannerContext, stream *aggOpStream) { + stream.values[0] = 1 + for i := 1; i < len(stream.values); i <<= 1 { + copy(stream.values[i:], stream.values[:i]) + } + }, + }) +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_agg_op.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_agg_op.go new file mode 100644 index 00000000..8eb1dee5 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_agg_op.go @@ -0,0 +1,70 @@ +package internal_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" +) + +type AggOpPlanner struct { + AggregatorPlanner + Func string +} + +func (a *AggOpPlanner) IsMatrix() bool { + return true +} +func (a *AggOpPlanner) Process(ctx *shared.PlannerContext, in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + switch a.Func { + case "stddev": + return nil, &shared.NotSupportedError{Msg: "stddev is not supported yet."} + case "stdvar": + return nil, &shared.NotSupportedError{Msg: "stdvar is not supported yet."} + } + return a.process(ctx, in, aggregatorPlannerOps{ + addValue: a.addValue, + finalize: a.finalize, + }) +} + +func (a *AggOpPlanner) addValue(ctx *shared.PlannerContext, entry *shared.LogEntry, stream *aggOpStream) { + idx := (entry.TimestampNS - ctx.From.UnixNano()) / a.Duration.Nanoseconds() + if idx < 0 || idx*2 > int64(len(stream.values)) { + return + } + switch a.Func { + case "sum": + stream.values[idx*2] += entry.Value + stream.values[idx*2+1] = 1 + case "min": + if stream.values[idx*2] > entry.Value || stream.values[idx*2+1] == 0 { + stream.values[idx*2] = entry.Value + stream.values[idx*2+1] = 1 + } + case "max": + if stream.values[idx*2] < entry.Value || stream.values[idx*2+1] == 0 { + stream.values[idx*2] = entry.Value + stream.values[idx*2+1] = 1 + } + case "avg": + stream.values[idx*2] += entry.Value + stream.values[idx*2+1]++ + case "count": + stream.values[idx*2]++ + stream.values[idx*2+1] = 1 + } +} + +func (a *AggOpPlanner) finalize(ctx *shared.PlannerContext, stream *aggOpStream) { + switch a.Func { + case "avg": + for i := 0; i < len(stream.values); i += 2 { + if stream.values[i+1] > 0 { + stream.values[i] /= stream.values[i+1] + } + } + } +} + +type aggOpStream struct { + labels map[string]string + values []float64 +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_by_without.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_by_without.go new file mode 100644 index 00000000..0141e9b6 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_by_without.go @@ -0,0 +1,45 @@ +package internal_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" +) + +type ByWithoutPlanner struct { + GenericPlanner + By bool + Labels []string + + labels map[string]bool +} + +func (a *ByWithoutPlanner) Process(ctx *shared.PlannerContext, + in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + a.labels = make(map[string]bool) + for _, l := range a.Labels { + a.labels[l] = true + } + + return a.WrapProcess(ctx, in, GenericPlannerOps{ + OnEntry: a.cutLabels, + OnAfterEntriesSlice: func(entries []shared.LogEntry, out chan []shared.LogEntry) error { + out <- entries + return nil + }, + OnAfterEntries: func(c chan []shared.LogEntry) error { + return nil + }, + }) +} + +func (a *ByWithoutPlanner) cutLabels(e *shared.LogEntry) error { + if e.Labels == nil { + return nil + } + for k := range e.Labels { + if (a.By && !a.labels[k]) || (!a.By && a.labels[k]) { + delete(e.Labels, k) + } + } + e.Fingerprint = fingerprint(e.Labels) + return nil +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_comparison.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_comparison.go new file mode 100644 index 00000000..6894286c --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_comparison.go @@ -0,0 +1,52 @@ +package internal_planner + +import "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + +type ComparisonPlanner struct { + GenericPlanner + Op string + Val float64 +} + +func (a *ComparisonPlanner) IsMatrix() bool { + return true +} + +func (a *ComparisonPlanner) Process(ctx *shared.PlannerContext, + in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + var _entries []shared.LogEntry + return a.WrapProcess(ctx, in, GenericPlannerOps{ + OnEntry: func(entry *shared.LogEntry) error { + if a.compare(ctx, entry) { + _entries = append(_entries, *entry) + } + return nil + }, + OnAfterEntriesSlice: func(entries []shared.LogEntry, out chan []shared.LogEntry) error { + out <- _entries + _entries = nil + return nil + }, + OnAfterEntries: func(c chan []shared.LogEntry) error { + return nil + }, + }) +} + +func (a *ComparisonPlanner) compare(ctx *shared.PlannerContext, e *shared.LogEntry) bool { + switch a.Op { + case ">": + return e.Value > a.Val + case ">=": + return e.Value >= a.Val + case "<": + return e.Value < a.Val + case "<=": + return e.Value <= a.Val + case "==": + return e.Value == a.Val + case "!=": + return e.Value != a.Val + } + return false +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_drop.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_drop.go new file mode 100644 index 00000000..e25fddd0 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_drop.go @@ -0,0 +1,43 @@ +package internal_planner + +import "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + +type DropPlanner struct { + GenericPlanner + Labels []string + Values []string +} + +func (a *DropPlanner) Process(ctx *shared.PlannerContext, + in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + + return a.WrapProcess(ctx, in, GenericPlannerOps{ + OnEntry: a.cutLabels, + OnAfterEntriesSlice: func(entries []shared.LogEntry, out chan []shared.LogEntry) error { + out <- entries + return nil + }, + OnAfterEntries: func(c chan []shared.LogEntry) error { + return nil + }, + }) +} + +func (a *DropPlanner) cutLabels(e *shared.LogEntry) error { + if e.Labels == nil { + return nil + } + recountFP := false + for k, v := range e.Labels { + for i, l := range a.Labels { + if k == l && (a.Values[i] == "" || v == a.Values[i]) { + delete(e.Labels, k) + recountFP = true + } + } + } + if recountFP { + e.Fingerprint = fingerprint(e.Labels) + } + return nil +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_fingerprint_optimizer.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_fingerprint_optimizer.go new file mode 100644 index 00000000..fbdaa7e1 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_fingerprint_optimizer.go @@ -0,0 +1,40 @@ +package internal_planner + +import "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + +type ResponseOptimizerPlanner struct { + GenericPlanner +} + +func (a *ResponseOptimizerPlanner) Process(ctx *shared.PlannerContext, + in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + fpMap := make(map[uint64][]shared.LogEntry) + size := 0 + return a.WrapProcess(ctx, in, GenericPlannerOps{ + OnEntry: func(entry *shared.LogEntry) error { + fpMap[entry.Fingerprint] = append(fpMap[entry.Fingerprint], *entry) + size++ + return nil + }, + OnAfterEntriesSlice: func(entries []shared.LogEntry, c chan []shared.LogEntry) error { + if size < 3000 { + return nil + } + for _, ents := range fpMap { + c <- ents + } + fpMap = make(map[uint64][]shared.LogEntry) + size = 0 + return nil + }, + OnAfterEntries: func(c chan []shared.LogEntry) error { + if size == 0 { + return nil + } + for _, ents := range fpMap { + c <- ents + } + return nil + }, + }) +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_generic.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_generic.go new file mode 100644 index 00000000..29745f93 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_generic.go @@ -0,0 +1,59 @@ +package internal_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "io" +) + +type GenericPlanner struct { + Main shared.RequestProcessor +} + +func (g *GenericPlanner) IsMatrix() bool { + return false +} +func (g *GenericPlanner) WrapProcess(ctx *shared.PlannerContext, + in chan []shared.LogEntry, ops GenericPlannerOps) (chan []shared.LogEntry, error) { + _in, err := g.Main.Process(ctx, in) + if err != nil { + return nil, err + } + out := make(chan []shared.LogEntry) + + go func() { + onErr := func(err error) { + out <- []shared.LogEntry{{Err: err}} + go func() { + for range _in { + } + }() + } + defer close(out) + defer func() { shared.TamePanic(out) }() + for entries := range _in { + for i := range entries { + err := ops.OnEntry(&entries[i]) + if err != nil && err != io.EOF { + onErr(err) + return + } + } + err := ops.OnAfterEntriesSlice(entries, out) + if err != nil && err != io.EOF { + onErr(err) + return + } + } + err := ops.OnAfterEntries(out) + if err != nil && err != io.EOF { + onErr(err) + } + }() + return out, nil +} + +type GenericPlannerOps struct { + OnEntry func(*shared.LogEntry) error + OnAfterEntriesSlice func([]shared.LogEntry, chan []shared.LogEntry) error + OnAfterEntries func(chan []shared.LogEntry) error +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_generic_aggregator.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_generic_aggregator.go new file mode 100644 index 00000000..9e82d91a --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_generic_aggregator.go @@ -0,0 +1,80 @@ +package internal_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "time" +) + +type AggregatorPlanner struct { + GenericPlanner + Duration time.Duration +} + +func (g *AggregatorPlanner) IsMatrix() bool { + return true +} + +func (p *AggregatorPlanner) process(ctx *shared.PlannerContext, + in chan []shared.LogEntry, ops aggregatorPlannerOps) (chan []shared.LogEntry, error) { + + streamLen := ctx.To.Sub(ctx.From).Nanoseconds() / p.Duration.Nanoseconds() + if streamLen > 4000000000 { + return nil, &shared.NotSupportedError{Msg: "stream length is too large. Please try increasing duration."} + } + + res := map[uint64]*aggOpStream{} + + return p.GenericPlanner.WrapProcess(ctx, in, GenericPlannerOps{ + OnEntry: func(entry *shared.LogEntry) error { + if entry.Err != nil { + return entry.Err + } + if _, ok := res[entry.Fingerprint]; !ok { + if len(res) >= 2000 { + return &shared.NotSupportedError{ + Msg: "Too many time-series. Please try changing `by / without` clause.", + } + } + res[entry.Fingerprint] = &aggOpStream{ + labels: entry.Labels, + values: make([]float64, streamLen*2), + } + if ops.initStream != nil { + ops.initStream(ctx, res[entry.Fingerprint]) + } + } + ops.addValue(ctx, entry, res[entry.Fingerprint]) + return nil + }, + OnAfterEntriesSlice: func(entries []shared.LogEntry, c chan []shared.LogEntry) error { + return nil + }, + OnAfterEntries: func(out chan []shared.LogEntry) error { + for k, v := range res { + ops.finalize(ctx, v) + entries := make([]shared.LogEntry, 0, len(v.values)/2) + for i := 0; i < len(v.values); i += 2 { + if v.values[i+1] > 0 { + entries = append(entries, shared.LogEntry{ + Fingerprint: k, + TimestampNS: ctx.From.Add(time.Duration(i/2) * p.Duration).UnixNano(), + Labels: v.labels, + Value: v.values[i], + }) + } + } + if len(entries) > 0 { + out <- entries + } + } + return nil + }, + }) + +} + +type aggregatorPlannerOps struct { + addValue func(ctx *shared.PlannerContext, entry *shared.LogEntry, stream *aggOpStream) + finalize func(ctx *shared.PlannerContext, stream *aggOpStream) + initStream func(ctx *shared.PlannerContext, stream *aggOpStream) +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_label_filter.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_label_filter.go new file mode 100644 index 00000000..c8a1987e --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_label_filter.go @@ -0,0 +1,171 @@ +package internal_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_parser" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "regexp" + "strconv" + "strings" +) + +type LabelFilterPlanner struct { + GenericPlanner + Filter *logql_parser.LabelFilter +} + +func (a *LabelFilterPlanner) Process(ctx *shared.PlannerContext, + in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + fn, err := a.makeFilter(a.Filter) + if err != nil { + return nil, err + } + var _entries []shared.LogEntry + return a.WrapProcess(ctx, in, GenericPlannerOps{ + OnEntry: func(entry *shared.LogEntry) error { + if fn(entry.Labels) { + _entries = append(_entries, *entry) + } + return nil + }, + OnAfterEntriesSlice: func(entries []shared.LogEntry, c chan []shared.LogEntry) error { + c <- _entries + _entries = nil + return nil + }, + OnAfterEntries: func(c chan []shared.LogEntry) error { + return nil + }, + }) +} + +func (a *LabelFilterPlanner) makeFilter(filter *logql_parser.LabelFilter) (func(map[string]string) bool, error) { + var ( + res func(map[string]string) bool + err error + ) + if filter.Head.SimpleHead != nil { + if contains([]string{"=", "=~", "!~"}, filter.Head.SimpleHead.Fn) || + (filter.Head.SimpleHead.Fn == "!=" && filter.Head.SimpleHead.StrVal != nil) { + res, err = a.stringSimpleFilter(filter.Head.SimpleHead) + if err != nil { + return nil, err + } + } else { + res, err = a.numberSimpleFilter(filter.Head.SimpleHead) + if err != nil { + return nil, err + } + } + } else { + res, err = a.makeFilter(filter.Head.ComplexHead) + if err != nil { + return nil, err + } + } + if filter.Tail == nil { + return res, nil + } + switch strings.ToLower(filter.Op) { + case "and": + fn2, err := a.makeFilter(filter.Tail) + if err != nil { + return nil, err + } + return func(m map[string]string) bool { + return res(m) && fn2(m) + }, nil + case "or": + fn2, err := a.makeFilter(filter.Tail) + if err != nil { + return nil, err + } + return func(m map[string]string) bool { + return res(m) || fn2(m) + }, nil + } + return res, nil +} + +func (a *LabelFilterPlanner) stringSimpleFilter(filter *logql_parser.SimpleLabelFilter, +) (func(map[string]string) bool, error) { + strVal, err := filter.StrVal.Unquote() + if err != nil { + return nil, err + } + + switch filter.Fn { + case "=": + return func(m map[string]string) bool { + return strVal == m[filter.Label.Name] + }, nil + case "!=": + return func(m map[string]string) bool { + return strVal != m[filter.Label.Name] + }, nil + case "=~": + re, err := regexp.Compile(strVal) + if err != nil { + return nil, err + } + return func(m map[string]string) bool { + return re.MatchString(m[filter.Label.Name]) + }, nil + case "!~": + re, err := regexp.Compile(strVal) + if err != nil { + return nil, err + } + return func(m map[string]string) bool { + return !re.MatchString(m[filter.Label.Name]) + }, nil + } + return nil, fmt.Errorf("invalid simple label filter") +} + +func (a *LabelFilterPlanner) numberSimpleFilter(filter *logql_parser.SimpleLabelFilter, +) (func(map[string]string) bool, error) { + iVal, err := strconv.ParseFloat(filter.NumVal, 64) + if err != nil { + return nil, err + } + + var fn func(float64) bool + switch filter.Fn { + case ">": + fn = func(val float64) bool { + return val > iVal + } + case ">=": + fn = func(val float64) bool { + return val >= iVal + } + case "<": + fn = func(val float64) bool { + return val < iVal + } + case "<=": + fn = func(val float64) bool { + return val <= iVal + } + case "==": + fn = func(val float64) bool { + return iVal == val + } + case "!=": + fn = func(val float64) bool { + return iVal != val + } + } + return func(m map[string]string) bool { + strVal := m[filter.Label.Name] + if strVal == "" { + return false + } + iVal, err := strconv.ParseFloat(strVal, 64) + if err != nil { + return false + } + return fn(iVal) + }, nil +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_label_format.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_label_format.go new file mode 100644 index 00000000..08817d48 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_label_format.go @@ -0,0 +1,60 @@ +package internal_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_parser" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" +) + +type LabelFormatPlanner struct { + GenericPlanner + LabelFormat *logql_parser.LabelFormat +} + +func (a *LabelFormatPlanner) Process(ctx *shared.PlannerContext, + in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + + var labelFns []func(map[string]string) map[string]string + for _, op := range a.LabelFormat.LabelFormatOps { + label := op.Label.Name + + if op.ConstVal != nil { + str, err := op.ConstVal.Unquote() + if err != nil { + return nil, err + } + + labelFns = append(labelFns, func(m map[string]string) map[string]string { + m[label] = str + return m + }) + continue + } + + change := op.LabelVal.Name + + labelFns = append(labelFns, func(m map[string]string) map[string]string { + val := m[change] + if val == "" { + return m + } + m[label] = val + return m + }) + } + + return a.WrapProcess(ctx, in, GenericPlannerOps{ + OnEntry: func(entry *shared.LogEntry) error { + for _, fn := range labelFns { + entry.Labels = fn(entry.Labels) + } + return nil + }, + OnAfterEntriesSlice: func(entries []shared.LogEntry, c chan []shared.LogEntry) error { + c <- entries + return nil + }, + OnAfterEntries: func(c chan []shared.LogEntry) error { + return nil + }, + }) +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_limit.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_limit.go new file mode 100644 index 00000000..4ad1db6e --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_limit.go @@ -0,0 +1,37 @@ +package internal_planner + +import "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + +type LimitPlanner struct { + GenericPlanner +} + +func (a *LimitPlanner) Process(ctx *shared.PlannerContext, + in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + sent := 0 + limit := int(ctx.Limit) + return a.WrapProcess(ctx, in, GenericPlannerOps{ + OnEntry: func(entry *shared.LogEntry) error { + return nil + }, + OnAfterEntriesSlice: func(entries []shared.LogEntry, c chan []shared.LogEntry) error { + if sent >= limit { + return nil + } + if sent+len(entries) < limit { + c <- entries + sent += len(entries) + return nil + } + c <- entries[:limit-sent] + if ctx.CancelCtx != nil { + ctx.CancelCtx() + } + sent = limit + return nil + }, + OnAfterEntries: func(c chan []shared.LogEntry) error { + return nil + }, + }) +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_line_filter.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_line_filter.go new file mode 100644 index 00000000..be1912df --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_line_filter.go @@ -0,0 +1,58 @@ +package internal_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "regexp" + "strings" +) + +type LineFilterPlanner struct { + GenericPlanner + Op string + Val string + + re *regexp.Regexp +} + +func (a *LineFilterPlanner) Process(ctx *shared.PlannerContext, + in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + if a.Op == "|~" || a.Op == "!~" { + var err error + a.re, err = regexp.Compile(a.Val) + if err != nil { + return nil, err + } + } + var _entries []shared.LogEntry + return a.WrapProcess(ctx, in, GenericPlannerOps{ + OnEntry: func(entry *shared.LogEntry) error { + if entry.Err != nil || a.compare(ctx, entry) { + _entries = append(_entries, *entry) + } + return nil + }, + OnAfterEntriesSlice: func(entries []shared.LogEntry, c chan []shared.LogEntry) error { + c <- _entries + _entries = nil + return nil + }, + OnAfterEntries: func(c chan []shared.LogEntry) error { + return nil + }, + }) +} + +func (a *LineFilterPlanner) compare(ctx *shared.PlannerContext, + in *shared.LogEntry) bool { + switch a.Op { + case "|=": + return strings.Contains(in.Message, a.Val) + case "!=": + return !strings.Contains(in.Message, a.Val) + case "|~": + return a.re.MatchString(in.Message) + case "!~": + return !a.re.MatchString(in.Message) + } + return false +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_line_format.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_line_format.go new file mode 100644 index 00000000..a8d78a5a --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_line_format.go @@ -0,0 +1,85 @@ +package internal_planner + +import ( + "bytes" + "github.com/Masterminds/sprig" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "regexp" + "strings" + "text/template" +) + +var functionMap = func() template.FuncMap { + res := template.FuncMap{ + "ToLower": strings.ToLower, + "ToUpper": strings.ToUpper, + "Replace": strings.Replace, + "Trim": strings.Trim, + "TrimLeft": strings.TrimLeft, + "TrimRight": strings.TrimRight, + "TrimPrefix": strings.TrimPrefix, + "TrimSuffix": strings.TrimSuffix, + "TrimSpace": strings.TrimSpace, + "regexReplaceAll": func(regex string, s string, repl string) string { + r := regexp.MustCompile(regex) + return r.ReplaceAllString(s, repl) + }, + "regexReplaceAllLiteral": func(regex string, s string, repl string) string { + r := regexp.MustCompile(regex) + return r.ReplaceAllLiteralString(s, repl) + }, + } + sprigFuncMap := sprig.GenericFuncMap() + for _, addFn := range []string{"lower", "upper", "title", "trunc", "substr", "contains", + "hasPrefix", "hasSuffix", "indent", "nindent", "replace", "repeat", "trim", + "trimAll", "trimSuffix", "trimPrefix", "int", "float64", "add", "sub", "mul", + "div", "mod", "addf", "subf", "mulf", "divf", "max", "min", "maxf", "minf", "ceil", "floor", + "round", "fromJson", "date", "toDate", "now", "unixEpoch", + } { + if function, ok := sprigFuncMap[addFn]; ok { + res[addFn] = function + } + } + return res +}() + +type LineFormatterPlanner struct { + GenericPlanner + Template string +} + +func (l *LineFormatterPlanner) Process(ctx *shared.PlannerContext, + in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + tpl, err := template.New("line").Option("missingkey=zero").Funcs(functionMap).Parse(l.Template) + if err != nil { + return nil, err + } + + var _entries []shared.LogEntry + i := 0 + return l.WrapProcess(ctx, in, GenericPlannerOps{ + OnEntry: func(entry *shared.LogEntry) error { + var buf bytes.Buffer + _labels := make(map[string]string) + for k, v := range entry.Labels { + _labels[k] = v + } + _labels["_entry"] = entry.Message + if err := tpl.Execute(&buf, _labels); err != nil { + return nil + } + entry.Message = buf.String() + _entries = append(_entries, *entry) + return nil + }, + OnAfterEntriesSlice: func(entries []shared.LogEntry, c chan []shared.LogEntry) error { + i += 100 + c <- _entries + _entries = make([]shared.LogEntry, 0, 100) + return nil + }, + OnAfterEntries: func(c chan []shared.LogEntry) error { + return nil + }, + }) +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_lra.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_lra.go new file mode 100644 index 00000000..bf073beb --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_lra.go @@ -0,0 +1,54 @@ +package internal_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" +) + +type LRAPlanner struct { + AggregatorPlanner + Func string +} + +func (l *LRAPlanner) Process(ctx *shared.PlannerContext, + in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + if l.Func == "absent_over_time" { + return (&AbsentOverTimePlanner{ + AggregatorPlanner: l.AggregatorPlanner, + }).Process(ctx, in) + } + return l.process(ctx, in, aggregatorPlannerOps{ + addValue: l.addValue, + finalize: l.finalize, + }) +} + +func (l *LRAPlanner) addValue(ctx *shared.PlannerContext, entry *shared.LogEntry, stream *aggOpStream) { + idx := (entry.TimestampNS - ctx.From.UnixNano()) / l.Duration.Nanoseconds() * 2 + switch l.Func { + case "rate": + stream.values[idx]++ + stream.values[idx+1] = 1 + case "count_over_time": + stream.values[idx]++ + stream.values[idx+1] = 1 + case "bytes_rate": + stream.values[idx] += float64(len(entry.Message)) + stream.values[idx+1] = 1 + case "bytes_over_time": + stream.values[idx] += float64(len(entry.Message)) + stream.values[idx+1] = 1 + } +} + +func (l *LRAPlanner) finalize(ctx *shared.PlannerContext, stream *aggOpStream) { + switch l.Func { + case "rate": + for i := 0; i < len(stream.values); i += 2 { + stream.values[i] /= float64(l.Duration.Milliseconds()) / 1000 + } + case "bytes_rate": + for i := 0; i < len(stream.values); i += 2 { + stream.values[i] /= float64(l.Duration.Milliseconds()) / 1000 + } + } +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_matrix_step.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_matrix_step.go new file mode 100644 index 00000000..3e2ebb80 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_matrix_step.go @@ -0,0 +1,59 @@ +package internal_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "time" +) + +type MatrixStepPlanner struct { + GenericPlanner + Duration time.Duration +} + +func (m *MatrixStepPlanner) IsMatrix() bool { + return true +} + +func (m *MatrixStepPlanner) Process(ctx *shared.PlannerContext, in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + var previousEntry *shared.LogEntry + var entries []shared.LogEntry + /*var err error + if ctx.Step.Nanoseconds() >= m.Duration.Nanoseconds() { + in, err = m.Main.Process(ctx, in) + if err != nil { + return nil, err + } + return in, nil + }*/ + return m.WrapProcess(ctx, in, GenericPlannerOps{ + OnEntry: func(entry *shared.LogEntry) error { + if previousEntry == nil { + previousEntry = entry + return nil + } + i := previousEntry.TimestampNS + for ; i <= previousEntry.TimestampNS+m.Duration.Nanoseconds() && i < ctx.To.UnixNano(); i += m.Duration.Nanoseconds() { + newEntry := *previousEntry + newEntry.TimestampNS = i + entries = append(entries, newEntry) + } + previousEntry = entry + return nil + }, + OnAfterEntriesSlice: func(entries []shared.LogEntry, c chan []shared.LogEntry) error { + i := previousEntry.TimestampNS + for ; i <= previousEntry.TimestampNS+m.Duration.Nanoseconds() && i < ctx.To.UnixNano(); i += m.Duration.Nanoseconds() { + newEntry := *previousEntry + newEntry.TimestampNS = i + entries = append(entries, newEntry) + } + c <- entries + entries = nil + previousEntry = nil + return nil + }, + OnAfterEntries: func(c chan []shared.LogEntry) error { + return nil + }, + }) +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_parser.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_parser.go new file mode 100644 index 00000000..d22f996f --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_parser.go @@ -0,0 +1,76 @@ +package internal_planner + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" +) + +type ParserPlanner struct { + GenericPlanner + Op string + ParameterNames []string + ParameterValues []string + + parameterTypedValues [][]any + logfmtFields map[string]string +} + +func (p *ParserPlanner) IsMatrix() bool { return false } + +func (p *ParserPlanner) Process(ctx *shared.PlannerContext, + in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + + p.parameterTypedValues = make([][]any, len(p.ParameterValues)) + for i, v := range p.ParameterValues { + var err error + p.parameterTypedValues[i], err = shared.JsonPathParamToTypedArray(v) + if err != nil { + return nil, err + } + } + + if len(p.ParameterNames) > 0 { + p.logfmtFields = make(map[string]string, len(p.ParameterNames)) + for i, name := range p.ParameterNames { + if len(p.parameterTypedValues[i]) == 0 { + continue + } + switch p.parameterTypedValues[i][0].(type) { + case string: + p.logfmtFields[p.parameterTypedValues[i][0].(string)] = name + } + } + } + + parser := p.json + switch p.Op { + case "json": + if len(p.ParameterNames) > 0 { + parser = p.jsonWithParams + } + break + case "logfmt": + parser = p.logfmt + default: + return nil, &shared.NotSupportedError{Msg: fmt.Sprintf("%s not supported", p.Op)} + } + + return p.WrapProcess(ctx, in, GenericPlannerOps{ + OnEntry: func(entry *shared.LogEntry) error { + if entry.Err != nil { + return nil + } + var err error + entry.Labels, err = parser(entry.Message, &entry.Labels) + entry.Fingerprint = fingerprint(entry.Labels) + return err + }, + OnAfterEntriesSlice: func(entries []shared.LogEntry, c chan []shared.LogEntry) error { + c <- entries + return nil + }, + OnAfterEntries: func(c chan []shared.LogEntry) error { + return nil + }, + }) +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_parser_json.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_parser_json.go new file mode 100644 index 00000000..1c3456cd --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_parser_json.go @@ -0,0 +1,166 @@ +package internal_planner + +import ( + "fmt" + "github.com/go-faster/jx" + _ "github.com/go-faster/jx" + "regexp" +) + +func (p *ParserPlanner) json(str string, labels *map[string]string) (map[string]string, error) { + dec := jx.DecodeStr(str) + if dec.Next() != jx.Object { + return nil, fmt.Errorf("not an object") + } + err := p.subDec(dec, "", labels) + return *labels, err +} + +func (p *ParserPlanner) subDec(dec *jx.Decoder, prefix string, labels *map[string]string) error { + return dec.Obj(func(d *jx.Decoder, key string) error { + _prefix := prefix + if _prefix != "" { + _prefix += "_" + } + _prefix += key + switch d.Next() { + case jx.Object: + return p.subDec(d, _prefix, labels) + case jx.String: + val, err := d.Str() + if err != nil { + return err + } + (*labels)[sanitizeLabel(_prefix)] = val + return nil + case jx.Array: + return d.Skip() + default: + raw, err := d.Raw() + if err != nil { + return err + } + (*labels)[sanitizeLabel(_prefix)] = raw.String() + return nil + } + }) +} + +type pathAhead struct { + label string + path []any +} + +type jsonPathProcessor struct { + labels *map[string]string +} + +func (p *ParserPlanner) jsonWithParams(str string, labels *map[string]string) (map[string]string, error) { + dec := jx.DecodeStr(str) + var pa []pathAhead + for i, path := range p.parameterTypedValues { + name := p.ParameterNames[i] + pa = append(pa, pathAhead{label: name, path: path}) + } + jpp := &jsonPathProcessor{labels: labels} + err := jpp.process(dec, pa) + if err != nil { + return nil, err + } + return *jpp.labels, nil +} + +func (j *jsonPathProcessor) process(dec *jx.Decoder, aheads []pathAhead) error { + switch dec.Next() { + case jx.Object: + return j.processObject(dec, aheads) + case jx.Array: + return j.processArray(dec, aheads) + case jx.String: + val, err := dec.Str() + if err != nil { + return err + } + for _, a := range aheads { + if len(a.path) == 0 { + (*j.labels)[a.label] = val + } + } + default: + raw, err := dec.Raw() + if err != nil { + return err + } + val := raw.String() + for _, a := range aheads { + if len(a.path) == 0 { + (*j.labels)[a.label] = val + } + } + } + return nil +} + +func (j *jsonPathProcessor) processObject(dec *jx.Decoder, aheads []pathAhead) error { + if len(aheads) == 0 { + return dec.Skip() + } + return dec.Obj(func(d *jx.Decoder, key string) error { + _aheads := filterAhead(key, aheads) + if len(_aheads) == 0 { + return dec.Skip() + } + var __aheads []pathAhead + for _, a := range _aheads { + __aheads = append(__aheads, pathAhead{label: a.label, path: a.path[1:]}) + } + return j.process(d, __aheads) + }) +} + +func (j *jsonPathProcessor) processArray(dec *jx.Decoder, aheads []pathAhead) error { + if len(aheads) == 0 { + return dec.Skip() + } + i := -1 + return dec.Arr(func(d *jx.Decoder) error { + i++ + _aheads := filterAhead(i, aheads) + if len(_aheads) == 0 { + return dec.Skip() + } + var __aheads []pathAhead + for _, a := range _aheads { + __aheads = append(__aheads, pathAhead{label: a.label, path: a.path[1:]}) + } + return j.process(dec, __aheads) + }) +} + +func typeCmp[T int | string](a any, b any) bool { + _a, ok1 := a.(T) + _b, ok2 := b.(T) + if !ok1 || !ok2 { + return false + } + return _a == _b +} + +func filterAhead(key any, aheads []pathAhead) []pathAhead { + var result []pathAhead + for _, a := range aheads { + if len(a.path) == 0 { + continue + } + if typeCmp[int](a.path[0], key) || typeCmp[string](a.path[0], key) { + result = append(result, a) + } + } + return result +} + +var sanitizeRe = regexp.MustCompile("[^a-zA-Z0-9_]") + +func sanitizeLabel(label string) string { + return sanitizeRe.ReplaceAllString(label, "_") +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_parser_json_test.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_parser_json_test.go new file mode 100644 index 00000000..3a7e2c65 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_parser_json_test.go @@ -0,0 +1,28 @@ +package internal_planner + +import ( + "fmt" + "github.com/go-faster/jx" + "testing" +) + +func TestPPJ(t *testing.T) { + pathsAndJsons := [][]any{ + {`{"a":"b"}`, []any{"a"}}, + {`{"a":{"b":"c"}}`, []any{"a", "b"}}, + {`{"a":["b","c"]}`, []any{"a", 0}}, + {`{"u": 1, "a":{"b":[2,"d"]}}`, []any{"a", "b", 0}}, + {`{"a":{"e":0, "b":{"c":"d"}}}`, []any{"a", "b", "c"}}, + {`["c","d"]`, []any{0}}, + {`["c","d"]`, []any{1}}, + } + for _, pj := range pathsAndJsons { + jpp := &jsonPathProcessor{labels: &map[string]string{}} + dec := jx.DecodeStr(pj[0].(string)) + err := jpp.process(dec, []pathAhead{{label: "a", path: pj[1].([]any)}}) + if err != nil { + t.Fatal(err) + } + fmt.Println(*jpp.labels) + } +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_parser_logfmt.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_parser_logfmt.go new file mode 100644 index 00000000..f2c05538 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_parser_logfmt.go @@ -0,0 +1,27 @@ +package internal_planner + +import ( + "github.com/kr/logfmt" +) + +func (p *ParserPlanner) logfmt(str string, labels *map[string]string) (map[string]string, error) { + err := logfmt.Unmarshal([]byte(str), &logFmtParser{labels: labels, fields: p.logfmtFields}) + return *labels, err +} + +type logFmtParser struct { + labels *map[string]string + fields map[string]string +} + +func (p *logFmtParser) HandleLogfmt(key, val []byte) error { + if p.fields != nil { + l := p.fields[string(key)] + if l != "" { + (*p.labels)[l] = string(val) + } + return nil + } + (*p.labels)[sanitizeLabel(string(key))] = string(val) + return nil +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_unwrap.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_unwrap.go new file mode 100644 index 00000000..aaa74862 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_unwrap.go @@ -0,0 +1,43 @@ +package internal_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "strconv" +) + +type UnwrapPlanner struct { + GenericPlanner + Label string +} + +func (l *UnwrapPlanner) Process(ctx *shared.PlannerContext, + in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + return l.WrapProcess(ctx, in, GenericPlannerOps{ + OnEntry: func(entry *shared.LogEntry) error { + if entry.Err != nil { + return nil + } + var val string + if l.Label == "_entry" { + val = entry.Message + } else { + val = entry.Labels[l.Label] + } + if val != "" { + fVal, err := strconv.ParseFloat(val, 64) + if err != nil { + return nil + } + entry.Value = fVal + } + return nil + }, + OnAfterEntriesSlice: func(entries []shared.LogEntry, c chan []shared.LogEntry) error { + c <- entries + return nil + }, + OnAfterEntries: func(c chan []shared.LogEntry) error { + return nil + }, + }) +} diff --git a/reader/logql/logql_transpiler_v2/internal_planner/planner_unwrap_agg.go b/reader/logql/logql_transpiler_v2/internal_planner/planner_unwrap_agg.go new file mode 100644 index 00000000..0ec7d7c5 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/internal_planner/planner_unwrap_agg.go @@ -0,0 +1,66 @@ +package internal_planner + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" +) + +type UnwrapAggPlanner struct { + AggregatorPlanner + Function string +} + +func (l *UnwrapAggPlanner) Process(ctx *shared.PlannerContext, + in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + return l.process(ctx, in, aggregatorPlannerOps{ + addValue: l.addValue, + finalize: l.finalize, + }) +} + +func (l *UnwrapAggPlanner) addValue(ctx *shared.PlannerContext, entry *shared.LogEntry, stream *aggOpStream) { + idx := (entry.TimestampNS - ctx.From.UnixNano()) / l.Duration.Nanoseconds() * 2 + switch l.Function { + case "rate": + stream.values[idx] += entry.Value + stream.values[idx+1] = 1 + case "sum_over_time": + stream.values[idx] += entry.Value + stream.values[idx+1] = 1 + case "avg_over_time": + stream.values[idx] += entry.Value + stream.values[idx+1]++ + case "max_over_time": + if stream.values[idx] < entry.Value || stream.values[idx+1] == 0 { + stream.values[idx] = entry.Value + stream.values[idx+1] = 1 + } + case "min_over_time": + if stream.values[idx] < entry.Value || stream.values[idx+1] == 0 { + stream.values[idx] = entry.Value + stream.values[idx+1] = 1 + } + case "first_over_time": + if stream.values[idx] == 0 { + stream.values[idx] = entry.Value + stream.values[idx+1] = 1 + } + case "last_over_time": + stream.values[idx] = entry.Value + stream.values[idx+1] = 1 + } +} + +func (l *UnwrapAggPlanner) finalize(ctx *shared.PlannerContext, stream *aggOpStream) { + switch l.Function { + case "rate": + for i := 0; i < len(stream.values); i += 2 { + stream.values[i] /= float64(l.Duration.Milliseconds()) / 1000 + } + case "avg_over_time": + for i := 0; i < len(stream.values); i += 2 { + if stream.values[i+1] != 0 { + stream.values[i] /= stream.values[i+1] + } + } + } +} diff --git a/reader/logql/logql_transpiler_v2/planner.go b/reader/logql/logql_transpiler_v2/planner.go new file mode 100644 index 00000000..8b08de5f --- /dev/null +++ b/reader/logql/logql_transpiler_v2/planner.go @@ -0,0 +1,185 @@ +package logql_transpiler_v2 + +import ( + "github.com/metrico/qryn/reader/logql/logql_parser" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/clickhouse_planner" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/internal_planner" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/plugins" + "reflect" +) + +const ( + BreakpointNo = -1 + BreakpointLra = -2 +) + +func Plan(script *logql_parser.LogQLScript) (shared.RequestProcessorChain, error) { + for _, plugin := range plugins.GetLogQLPlannerPlugins() { + res, err := plugin.Plan(script) + if err == nil { + return res, nil + } + } + + breakpoint, err := GetBreakpoint(script) + if err != nil { + return nil, err + } + + var proc shared.RequestProcessor + if breakpoint == BreakpointNo || clickhouse_planner.AnalyzeMetrics15sShortcut(script) { + plan, err := clickhouse_planner.Plan(script, true) + if err != nil { + return nil, err + } + + proc = &shared.ClickhouseGetterPlanner{ + ClickhouseRequestPlanner: plan, + Matrix: script.StrSelector == nil, + } + + } else { + chScript, internalScript, err := breakScript(breakpoint, script, script) + if err != nil { + return nil, err + } + plan, err := clickhouse_planner.Plan(chScript, false) + if err != nil { + return nil, err + } + proc = &shared.ClickhouseGetterPlanner{ + ClickhouseRequestPlanner: plan, + Matrix: chScript.StrSelector == nil, + } + + proc, err = internal_planner.Plan(internalScript, proc) + if err != nil { + return nil, err + } + } + + proc, err = MatrixPostProcessors(script, proc) + return shared.RequestProcessorChain{proc}, err +} + +func MatrixPostProcessors(script *logql_parser.LogQLScript, + proc shared.RequestProcessor) (shared.RequestProcessor, error) { + if !proc.IsMatrix() { + return proc, nil + } + duration, err := shared.GetDuration(script) + if err != nil { + return nil, err + } + proc = &ZeroEaterPlanner{internal_planner.GenericPlanner{proc}} + proc = &FixPeriodPlanner{ + Main: proc, + Duration: duration, + } + return proc, nil +} + +func PlanFingerprints(script *logql_parser.LogQLScript) (shared.SQLRequestPlanner, error) { + return clickhouse_planner.PlanFingerprints(script) +} + +func GetBreakpoint(node any) (int, error) { + dfs := func(node ...any) (int, error) { + for _, n := range node { + if n != nil && !reflect.ValueOf(n).IsNil() { + return GetBreakpoint(n) + } + } + return BreakpointNo, nil + } + + switch node.(type) { + case *logql_parser.LogQLScript: + script := node.(*logql_parser.LogQLScript) + return dfs(script.TopK, script.QuantileOverTime, script.AggOperator, script.LRAOrUnwrap, + script.StrSelector) + case *logql_parser.TopK: + script := node.(*logql_parser.TopK) + return dfs(script.QuantileOverTime, script.AggOperator, script.LRAOrUnwrap) + case *logql_parser.QuantileOverTime: + script := node.(*logql_parser.QuantileOverTime) + return dfs(&script.StrSel) + case *logql_parser.AggOperator: + script := node.(*logql_parser.AggOperator) + return dfs(&script.LRAOrUnwrap) + case *logql_parser.LRAOrUnwrap: + script := node.(*logql_parser.LRAOrUnwrap) + bp, err := dfs(&script.StrSel) + if script.Fn == "absent_over_time" && bp < 0 && err == nil { + return BreakpointLra, nil + } + return bp, err + case *logql_parser.StrSelector: + script := node.(*logql_parser.StrSelector) + for i, ppl := range script.Pipelines { + if ppl.Parser != nil && + ((ppl.Parser.Fn == "json" && len(ppl.Parser.ParserParams) == 0) || + ppl.Parser.Fn == "logfmt") { + return i, nil + } + if ppl.LineFormat != nil { + return i, nil + } + } + return BreakpointNo, nil + } + return BreakpointNo, nil +} + +func breakScript(breakpoint int, script *logql_parser.LogQLScript, + node any) (*logql_parser.LogQLScript, *logql_parser.LogQLScript, error) { + dfs := func(node ...any) (*logql_parser.LogQLScript, *logql_parser.LogQLScript, error) { + for _, n := range node { + if n != nil && !reflect.ValueOf(n).IsNil() { + return breakScript(breakpoint, script, n) + } + } + return script, nil, nil + } + switch node.(type) { + case *logql_parser.LogQLScript: + _script := node.(*logql_parser.LogQLScript) + return dfs(_script.TopK, _script.AggOperator, _script.StrSelector, _script.LRAOrUnwrap, + _script.QuantileOverTime) + case *logql_parser.TopK: + return nil, nil, &shared.NotSupportedError{Msg: "TopK is not supported for this query"} + case *logql_parser.AggOperator: + _script := node.(*logql_parser.AggOperator) + return dfs(&_script.LRAOrUnwrap) + case *logql_parser.StrSelector: + _script := node.(*logql_parser.StrSelector) + if breakpoint < 0 { + return script, nil, nil + } + chScript := &logql_parser.LogQLScript{ + StrSelector: &logql_parser.StrSelector{ + StrSelCmds: _script.StrSelCmds, + Pipelines: _script.Pipelines[:breakpoint], + }, + } + _script.Pipelines = _script.Pipelines[breakpoint:] + return chScript, script, nil + case *logql_parser.LRAOrUnwrap: + _script := node.(*logql_parser.LRAOrUnwrap) + if breakpoint != BreakpointLra { + return dfs(&_script.StrSel) + } + chScript := &logql_parser.LogQLScript{ + StrSelector: &logql_parser.StrSelector{ + StrSelCmds: _script.StrSel.StrSelCmds, + Pipelines: _script.StrSel.Pipelines, + }, + } + _script.StrSel = logql_parser.StrSelector{} + return chScript, script, nil + case *logql_parser.QuantileOverTime: + return nil, nil, &shared.NotSupportedError{Msg: "QuantileOverTime is not supported for this query"} + } + return nil, nil, nil +} diff --git a/reader/logql/logql_transpiler_v2/planner_from_fix.go b/reader/logql/logql_transpiler_v2/planner_from_fix.go new file mode 100644 index 00000000..8c9ad5de --- /dev/null +++ b/reader/logql/logql_transpiler_v2/planner_from_fix.go @@ -0,0 +1,93 @@ +package logql_transpiler_v2 + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "time" +) + +type FixPeriodPlanner struct { + Main shared.RequestProcessor + Duration time.Duration +} + +func (m *FixPeriodPlanner) IsMatrix() bool { + return true +} +func (m *FixPeriodPlanner) Process(ctx *shared.PlannerContext, + in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + _from := ctx.From.UnixNano() + _to := ctx.To.UnixNano() + ctx.From = ctx.From.Truncate(m.Duration) + ctx.To = ctx.To.Truncate(m.Duration).Add(m.Duration) + + _in, err := m.Main.Process(ctx, in) + if err != nil { + return nil, err + } + + res := make(chan []shared.LogEntry) + + var ( + values []float64 + fingerprint uint64 + labels map[string]string + ) + + exportEntries := func() { + entries := make([]shared.LogEntry, 0, len(values)) + for i, v := range values { + if v == 0 { + continue + } + entries = append(entries, shared.LogEntry{ + TimestampNS: _from + int64(i)*ctx.Step.Nanoseconds(), + Fingerprint: fingerprint, + Labels: labels, + Message: "", + Value: v, + Err: nil, + }) + } + if len(entries) > 0 { + res <- entries + } + } + + go func() { + defer close(res) + for entries := range _in { + for _, entry := range entries { + if entry.Fingerprint != fingerprint { + exportEntries() + fingerprint = entry.Fingerprint + values = make([]float64, (_to-_from)/ctx.Step.Nanoseconds()+1) + labels = entry.Labels + } + idxFrom := ((entry.TimestampNS/m.Duration.Nanoseconds())*m.Duration.Nanoseconds() - _from) / ctx.Step.Nanoseconds() + idxTo := ((entry.TimestampNS/m.Duration.Nanoseconds()+1)*m.Duration.Nanoseconds() - _from) / ctx.Step.Nanoseconds() + + if idxTo < 0 || idxFrom >= int64(len(values)) { + continue + } + if idxFrom < 0 { + idxFrom = 0 + } + if idxTo >= int64(len(values)) { + idxTo = int64(len(values)) - 1 + } + fastFill(values[idxFrom:idxTo+1], entry.Value) + } + } + exportEntries() + return + }() + return res, nil +} + +func fastFill(v []float64, val float64) { + v[0] = val + l := 1 + for ; l < len(v); l *= 2 { + copy(v[l:], v[:l]) + } +} diff --git a/reader/logql/logql_transpiler_v2/planner_matrix_step.go b/reader/logql/logql_transpiler_v2/planner_matrix_step.go new file mode 100644 index 00000000..a6233c48 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/planner_matrix_step.go @@ -0,0 +1,53 @@ +package logql_transpiler_v2 + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "time" +) + +type MatrixStepPlanner struct { + Main shared.RequestProcessor + Duration time.Duration +} + +func (m *MatrixStepPlanner) IsMatrix() bool { + return true +} +func (m *MatrixStepPlanner) Process(ctx *shared.PlannerContext, + in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + _in, err := m.Main.Process(ctx, in) + if err != nil { + return nil, err + } + out := make(chan []shared.LogEntry) + go func() { + defer close(out) + defer func() { shared.TamePanic(out) }() + var ( + fp uint64 + nextTsNs int64 + ) + + for entries := range _in { + var _entries []shared.LogEntry + for _, entry := range entries { + if entry.Fingerprint != fp { + nextTsNs = 0 + fp = entry.Fingerprint + } + if entry.TimestampNS < nextTsNs { + continue + } + start := entry.TimestampNS + i := entry.TimestampNS + for ; i < start+m.Duration.Nanoseconds() && i < ctx.To.UnixNano(); i += ctx.Step.Nanoseconds() { + entry.TimestampNS = i + _entries = append(_entries, entry) + } + nextTsNs = start + m.Duration.Nanoseconds() + } + out <- _entries + } + }() + return out, nil +} diff --git a/reader/logql/logql_transpiler_v2/planner_zero_eater.go b/reader/logql/logql_transpiler_v2/planner_zero_eater.go new file mode 100644 index 00000000..4fcafb15 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/planner_zero_eater.go @@ -0,0 +1,36 @@ +package logql_transpiler_v2 + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/internal_planner" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" +) + +type ZeroEaterPlanner struct { + internal_planner.GenericPlanner +} + +func (m *ZeroEaterPlanner) IsMatrix() bool { + return true +} +func (m *ZeroEaterPlanner) Process(ctx *shared.PlannerContext, + in chan []shared.LogEntry) (chan []shared.LogEntry, error) { + var _entries []shared.LogEntry + return m.WrapProcess(ctx, in, internal_planner.GenericPlannerOps{ + OnEntry: func(entry *shared.LogEntry) error { + if entry.Value != 0 { + _entries = append(_entries, *entry) + } + return nil + }, + OnAfterEntriesSlice: func(entries []shared.LogEntry, c chan []shared.LogEntry) error { + if len(_entries) > 0 { + c <- _entries + _entries = nil + } + return nil + }, + OnAfterEntries: func(c chan []shared.LogEntry) error { + return nil + }, + }) +} diff --git a/reader/logql/logql_transpiler_v2/shared/errors.go b/reader/logql/logql_transpiler_v2/shared/errors.go new file mode 100644 index 00000000..f0d4cd80 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/shared/errors.go @@ -0,0 +1,28 @@ +package shared + +import ( + "fmt" + "github.com/metrico/qryn/reader/utils/logger" + "runtime/debug" +) + +type NotSupportedError struct { + Msg string +} + +func (n *NotSupportedError) Error() string { + return n.Msg +} + +func isNotSupportedError(e error) bool { + _, ok := e.(*NotSupportedError) + return ok +} + +func TamePanic(out chan []LogEntry) { + if err := recover(); err != nil { + logger.Error(err, " stack:", string(debug.Stack())) + out <- []LogEntry{{Err: fmt.Errorf("panic: %v", err)}} + recover() + } +} diff --git a/reader/logql/logql_transpiler_v2/shared/funcs.go b/reader/logql/logql_transpiler_v2/shared/funcs.go new file mode 100644 index 00000000..0bb58b0e --- /dev/null +++ b/reader/logql/logql_transpiler_v2/shared/funcs.go @@ -0,0 +1,73 @@ +package shared + +import ( + "github.com/metrico/qryn/reader/logql/logql_parser" + "reflect" + "time" +) + +func GetDuration(script any) (time.Duration, error) { + dfs := func(node ...any) (time.Duration, error) { + for _, n := range node { + if n != nil && !reflect.ValueOf(n).IsNil() { + res, err := GetDuration(n) + if err != nil { + return 0, err + } + if res.Nanoseconds() != 0 { + return res, nil + } + } + } + return 0, nil + } + + switch script.(type) { + case *logql_parser.LogQLScript: + script := script.(*logql_parser.LogQLScript) + return dfs(script.AggOperator, script.LRAOrUnwrap, script.TopK, script.QuantileOverTime) + case *logql_parser.LRAOrUnwrap: + script := script.(*logql_parser.LRAOrUnwrap) + return time.ParseDuration(script.Time + script.TimeUnit) + case *logql_parser.AggOperator: + return GetDuration(&script.(*logql_parser.AggOperator).LRAOrUnwrap) + case *logql_parser.TopK: + script := script.(*logql_parser.TopK) + return dfs(script.LRAOrUnwrap, script.QuantileOverTime, script.AggOperator) + case *logql_parser.QuantileOverTime: + script := script.(*logql_parser.QuantileOverTime) + return time.ParseDuration(script.Time + script.TimeUnit) + } + return 0, nil +} + +func GetStrSelector(script any) *logql_parser.StrSelector { + dfs := func(node ...any) *logql_parser.StrSelector { + for _, n := range node { + if n != nil && !reflect.ValueOf(n).IsNil() { + return GetStrSelector(n) + } + } + return nil + } + + switch script.(type) { + case *logql_parser.LogQLScript: + script := script.(*logql_parser.LogQLScript) + return dfs(script.StrSelector, script.TopK, script.AggOperator, script.LRAOrUnwrap, script.QuantileOverTime) + case *logql_parser.StrSelector: + return script.(*logql_parser.StrSelector) + case *logql_parser.TopK: + script := script.(*logql_parser.TopK) + return dfs(script.QuantileOverTime, script.LRAOrUnwrap, script.AggOperator) + case *logql_parser.AggOperator: + script := script.(*logql_parser.AggOperator) + return dfs(&script.LRAOrUnwrap) + case *logql_parser.LRAOrUnwrap: + return &script.(*logql_parser.LRAOrUnwrap).StrSel + case *logql_parser.QuantileOverTime: + script := script.(*logql_parser.QuantileOverTime) + return &script.StrSel + } + return nil +} diff --git a/reader/logql/logql_transpiler_v2/shared/path_parser.go b/reader/logql/logql_transpiler_v2/shared/path_parser.go new file mode 100644 index 00000000..aca5652e --- /dev/null +++ b/reader/logql/logql_transpiler_v2/shared/path_parser.go @@ -0,0 +1,117 @@ +package shared + +import ( + "fmt" + "github.com/alecthomas/participle/v2" + "github.com/alecthomas/participle/v2/lexer" + "github.com/metrico/qryn/reader/logql/logql_parser" + "io" + "strconv" + "text/scanner" +) + +func JsonPathParamToTypedArray(param string) ([]any, error) { + parser, err := participle.Build[jsonPath](participle.Lexer(&jsonDefinitionImpl{})) + if err != nil { + return []any{}, err + } + oPath, err := parser.ParseString("", param) + if err != nil { + return []any{}, err + } + parts := make([]any, len(oPath.Path)) + for i, part := range oPath.Path { + parts[i], err = part.ToPathPart() + if err != nil { + return []any{}, err + } + } + return parts, nil +} + +func JsonPathParamToArray(param string) ([]string, error) { + parser, err := participle.Build[jsonPath](participle.Lexer(&jsonDefinitionImpl{})) + if err != nil { + return []string{}, err + } + oPath, err := parser.ParseString("", param) + if err != nil { + return []string{}, err + } + parts := make([]string, len(oPath.Path)) + for i, part := range oPath.Path { + parts[i], err = part.String() + if err != nil { + return []string{}, err + } + } + return parts, nil +} + +type jsonPath struct { + Path []jsonPathPart `@@+` +} + +type jsonPathPart struct { + Ident string `Dot? @Ident` + Field string `| OSQBrack @(QStr|TStr) CSQBrack` + Idx string `| OSQBrack @Int CSQBrack` +} + +func (j *jsonPathPart) String() (string, error) { + if j.Ident != "" { + return j.Ident, nil + } + if j.Idx != "" { + i, err := strconv.Atoi(j.Idx) + return fmt.Sprintf("%d", i+1), err + } + return (&logql_parser.QuotedString{Str: j.Field}).Unquote() +} + +func (j *jsonPathPart) ToPathPart() (any, error) { + if j.Ident != "" { + return j.Ident, nil + } + if j.Field != "" { + if j.Field[0] == '"' { + return strconv.Unquote(j.Field) + } + return j.Field[1 : len(j.Field)-1], nil + } + return strconv.Atoi(j.Idx) +} + +func (j *jsonPathPart) Value() (any, error) { + if j.Ident != "" { + return j.Ident, nil + } + if j.Idx != "" { + i, err := strconv.Atoi(j.Idx) + return i, err + } + return (&logql_parser.QuotedString{Str: j.Field}).Unquote() +} + +/* ---------- JSON parser ---------------------*/ +var symbols = map[string]lexer.TokenType{ + "Ident": -2, + "Dot": 46, + "OSQBrack": 91, + "CSQBrack": 93, + "QStr": -6, + "TStr": -7, + "Int": -3, +} + +type jsonDefinitionImpl struct{} + +func (j *jsonDefinitionImpl) Symbols() map[string]lexer.TokenType { + return symbols +} + +func (j *jsonDefinitionImpl) Lex(filename string, r io.Reader) (lexer.Lexer, error) { + s := scanner.Scanner{} + s.Init(r) + return lexer.LexWithScanner("", &s), nil +} diff --git a/reader/logql/logql_transpiler_v2/shared/planner_clickhouse_getter.go b/reader/logql/logql_transpiler_v2/shared/planner_clickhouse_getter.go new file mode 100644 index 00000000..00dfeee4 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/shared/planner_clickhouse_getter.go @@ -0,0 +1,120 @@ +package shared + +import ( + "database/sql" + sql2 "github.com/metrico/qryn/reader/utils/sql_select" + "io" +) + +type ClickhouseGetterPlanner struct { + ClickhouseRequestPlanner SQLRequestPlanner + Matrix bool +} + +func (c *ClickhouseGetterPlanner) IsMatrix() bool { + return c.Matrix +} + +func (c *ClickhouseGetterPlanner) Process(ctx *PlannerContext, + ch chan []LogEntry) (chan []LogEntry, error) { + req, err := c.ClickhouseRequestPlanner.Process(ctx) + if err != nil { + return nil, err + } + var options []int + if ctx.IsCluster { + options = append(options, sql2.STRING_OPT_INLINE_WITH) + } + strReq, err := req.String(ctx.CHSqlCtx, options...) + rows, err := ctx.CHDb.QueryCtx(ctx.Ctx, strReq) + if err != nil { + return nil, err + } + res := make(chan []LogEntry) + if !c.Matrix { + go c.Scan(ctx, rows, res) + } else { + go c.ScanMatrix(ctx, rows, res) + } + + return res, nil +} + +func (c *ClickhouseGetterPlanner) Scan(ctx *PlannerContext, rows *sql.Rows, res chan []LogEntry) { + defer rows.Close() + defer close(res) + entries := make([]LogEntry, 100) + i := 0 + + for rows.Next() { + select { + case <-ctx.Ctx.Done(): + if len(entries) > 0 { + res <- entries[:i] + } + return + default: + } + var ( + labels map[string]string + ) + err := rows.Scan(&entries[i].Fingerprint, &labels, &entries[i].Message, &entries[i].TimestampNS) + if err != nil { + entries[i].Err = err + res <- entries[:i+1] + return + } + entries[i].Labels = make(map[string]string) + for k, v := range labels { + entries[i].Labels[k] = v + } + i++ + if i >= 100 { + res <- entries + entries = make([]LogEntry, 100) + i = 0 + } + } + entries[i].Err = io.EOF + res <- entries[:i+1] +} + +func (c *ClickhouseGetterPlanner) ScanMatrix(ctx *PlannerContext, rows *sql.Rows, res chan []LogEntry) { + defer rows.Close() + defer close(res) + entries := make([]LogEntry, 100) + i := 0 + + for rows.Next() { + select { + case <-ctx.Ctx.Done(): + if len(entries) > 0 { + res <- entries[:i] + } + return + default: + } + var ( + labels map[string]string + ) + err := rows.Scan(&entries[i].Fingerprint, &labels, &entries[i].Value, + &entries[i].TimestampNS) + if err != nil { + entries[i].Err = err + res <- entries[:i+1] + return + } + entries[i].Labels = make(map[string]string) + for k, v := range labels { + entries[i].Labels[k] = v + } + i++ + if i >= 100 { + res <- entries + entries = make([]LogEntry, 100) + i = 0 + } + } + entries[i].Err = io.EOF + res <- entries[:i+1] +} diff --git a/reader/logql/logql_transpiler_v2/shared/tempo_types.go b/reader/logql/logql_transpiler_v2/shared/tempo_types.go new file mode 100644 index 00000000..5e8716d4 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/shared/tempo_types.go @@ -0,0 +1,11 @@ +package shared + +import "github.com/metrico/qryn/reader/model" + +type GenericTraceRequestProcessor[T any] interface { + Process(*PlannerContext) (chan []T, error) +} + +type TraceRequestProcessor interface { + Process(*PlannerContext) (chan []model.TraceInfo, error) +} diff --git a/reader/logql/logql_transpiler_v2/shared/types.go b/reader/logql/logql_transpiler_v2/shared/types.go new file mode 100644 index 00000000..87ef86b7 --- /dev/null +++ b/reader/logql/logql_transpiler_v2/shared/types.go @@ -0,0 +1,101 @@ +package shared + +import ( + "context" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/utils/dbVersion" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "time" +) + +const SAMPLES_TYPE_LOGS = 1 +const SAMPLES_TYPE_METRICS = 2 +const SAMPLES_TYPE_BOTH = 0 + +type RequestProcessor interface { + IsMatrix() bool + Process(*PlannerContext, chan []LogEntry) (chan []LogEntry, error) +} + +type RandomFilter struct { + Max int + I int +} + +type PlannerContext struct { + IsCluster bool + From time.Time + To time.Time + OrderASC bool + Limit int64 + + TimeSeriesGinTableName string + SamplesTableName string + TimeSeriesTableName string + TimeSeriesDistTableName string + Metrics15sTableName string + + TracesAttrsTable string + TracesAttrsDistTable string + TracesTable string + TracesDistTable string + TracesKVTable string + TracesKVDistTable string + + ProfilesSeriesGinTable string + ProfilesSeriesGinDistTable string + ProfilesTable string + ProfilesDistTable string + ProfilesSeriesTable string + ProfilesSeriesDistTable string + + UseCache bool + + Ctx context.Context + + CHDb model.ISqlxDB + CHFinalize bool + CHSqlCtx *sql.Ctx + + DDBSamplesTable string + DDBTSTable string + + CancelCtx context.CancelFunc + + Step time.Duration + + DeleteID string + + Type uint8 + + id int + + RandomFilter RandomFilter + CachedTraceIds []string + VersionInfo dbVersion.VersionInfo +} + +func (p *PlannerContext) Id() int { + p.id++ + return p.id +} + +type SQLRequestPlanner interface { + Process(ctx *PlannerContext) (sql.ISelect, error) +} + +type LogEntry struct { + TimestampNS int64 + Fingerprint uint64 + Labels map[string]string + Message string + Value float64 + + Err error +} + +type RequestProcessorChain []RequestProcessor + +type RequestPlanner interface { + Process(cnain RequestProcessorChain) (RequestProcessorChain, error) +} diff --git a/reader/logql/logql_transpiler_v2/transpiler.go b/reader/logql/logql_transpiler_v2/transpiler.go new file mode 100644 index 00000000..979e9b7e --- /dev/null +++ b/reader/logql/logql_transpiler_v2/transpiler.go @@ -0,0 +1,14 @@ +package logql_transpiler_v2 + +import ( + "github.com/metrico/qryn/reader/logql/logql_parser" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" +) + +func Transpile(script string) (shared.RequestProcessorChain, error) { + oScript, err := logql_parser.Parse(script) + if err != nil { + return nil, err + } + return Plan(oScript) +} diff --git a/reader/logql/logql_transpiler_v2/types.go b/reader/logql/logql_transpiler_v2/types.go new file mode 100644 index 00000000..b1b3ffaa --- /dev/null +++ b/reader/logql/logql_transpiler_v2/types.go @@ -0,0 +1 @@ +package logql_transpiler_v2 diff --git a/reader/main.go b/reader/main.go new file mode 100644 index 00000000..b6d5490d --- /dev/null +++ b/reader/main.go @@ -0,0 +1,101 @@ +package reader + +import ( + "fmt" + _ "github.com/ClickHouse/clickhouse-go/v2" + "github.com/gorilla/mux" + _ "github.com/gorilla/mux" + clconfig "github.com/metrico/cloki-config" + "github.com/metrico/qryn/reader/config" + "github.com/metrico/qryn/reader/dbRegistry" + "github.com/metrico/qryn/reader/model" + apirouterv1 "github.com/metrico/qryn/reader/router" + "github.com/metrico/qryn/reader/utils/logger" + "github.com/metrico/qryn/reader/utils/middleware" + "github.com/metrico/qryn/reader/watchdog" + "net" + "net/http" + "runtime" +) + +var ownHttpServer bool = false + +func Init(cnf *clconfig.ClokiConfig, app *mux.Router) { + config.Cloki = cnf + + //Set to max cpu if the value is equals 0 + if config.Cloki.Setting.SYSTEM_SETTINGS.CPUMaxProcs == 0 { + runtime.GOMAXPROCS(runtime.NumCPU()) + } else { + runtime.GOMAXPROCS(config.Cloki.Setting.SYSTEM_SETTINGS.CPUMaxProcs) + } + + // initialize logger + // + logger.InitLogger() + + if app == nil { + app = mux.NewRouter() + ownHttpServer = true + } + + //Api + // configure to serve WebServices + configureAsHTTPServer(app) +} + +func configureAsHTTPServer(acc *mux.Router) { + httpURL := fmt.Sprintf("%s:%d", config.Cloki.Setting.HTTP_SETTINGS.Host, config.Cloki.Setting.HTTP_SETTINGS.Port) + applyMiddlewares(acc) + + performV1APIRouting(acc) + + if ownHttpServer { + httpStart(acc, httpURL) + } +} + +func applyMiddlewares(acc *mux.Router) { + if !ownHttpServer { + return + } + if config.Cloki.Setting.AUTH_SETTINGS.BASIC.Username != "" && + config.Cloki.Setting.AUTH_SETTINGS.BASIC.Password != "" { + acc.Use(middleware.BasicAuthMiddleware(config.Cloki.Setting.AUTH_SETTINGS.BASIC.Username, + config.Cloki.Setting.AUTH_SETTINGS.BASIC.Password)) + } + acc.Use(middleware.AcceptEncodingMiddleware) + if config.Cloki.Setting.HTTP_SETTINGS.Cors.Enable { + acc.Use(middleware.CorsMiddleware(config.Cloki.Setting.HTTP_SETTINGS.Cors.Origin)) + } + acc.Use(middleware.LoggingMiddleware("[{{.status}}] {{.method}} {{.url}} - LAT:{{.latency}}")) +} + +func httpStart(server *mux.Router, httpURL string) { + logger.Info("Starting service") + http.Handle("/", server) + listener, err := net.Listen("tcp", httpURL) + if err != nil { + logger.Error("Error creating listener:", err) + panic(err) + } + logger.Info("Server is listening on", httpURL) + if err := http.Serve(listener, server); err != nil { + logger.Error("Error serving:", err) + panic(err) + } +} + +func performV1APIRouting(acc *mux.Router) { + dbRegistry.Init() + watchdog.Init(&model.ServiceData{Session: dbRegistry.Registry}) + + apirouterv1.RouteQueryRangeApis(acc, dbRegistry.Registry) + apirouterv1.RouteSelectLabels(acc, dbRegistry.Registry) + apirouterv1.RouteSelectPrometheusLabels(acc, dbRegistry.Registry) + apirouterv1.RoutePrometheusQueryRange(acc, dbRegistry.Registry, config.Cloki.Setting.SYSTEM_SETTINGS.QueryStats) + apirouterv1.RouteTempo(acc, dbRegistry.Registry) + apirouterv1.RouteMiscApis(acc) + apirouterv1.RouteProf(acc, dbRegistry.Registry) + apirouterv1.PluggableRoutes(acc, dbRegistry.Registry) +} diff --git a/reader/model/ISqlxDB.go b/reader/model/ISqlxDB.go new file mode 100644 index 00000000..52203b5a --- /dev/null +++ b/reader/model/ISqlxDB.go @@ -0,0 +1,16 @@ +package model + +import ( + "context" + "database/sql" +) + +type ISqlxDB interface { + GetName() string + /*Query(query string, args ...any) (*sql.Rows, error)*/ + QueryCtx(ctx context.Context, query string, args ...any) (*sql.Rows, error) + ExecCtx(ctx context.Context, query string, args ...any) error + Conn(ctx context.Context) (*sql.Conn, error) + Begin() (*sql.Tx, error) + Close() +} diff --git a/reader/model/databasesMapModel.go b/reader/model/databasesMapModel.go new file mode 100644 index 00000000..31ea2345 --- /dev/null +++ b/reader/model/databasesMapModel.go @@ -0,0 +1,27 @@ +package model + +import "github.com/metrico/cloki-config/config" + +type DataDatabasesMap struct { + Config *config.ClokiBaseDataBase + DSN string `json:"dsn"` + Session ISqlxDB +} + +type ConfigDatabasesMap struct { + Value string `json:"value"` + Name string `json:"name"` + Node string `json:"node"` + Host string `json:"host"` + Primary bool `json:"primary"` + Online bool `json:"online"` + URL string `json:"url"` + ProtectedTables []string `json:"-"` + SkipTables []string `json:"-"` +} + +type ConfigURLNode struct { + Name string `json:"name"` + URL string `json:"url"` + Primary bool `json:"primary"` +} diff --git a/reader/model/iDBRegistry.go b/reader/model/iDBRegistry.go new file mode 100644 index 00000000..bad2c067 --- /dev/null +++ b/reader/model/iDBRegistry.go @@ -0,0 +1,12 @@ +package model + +import ( + "context" +) + +type IDBRegistry interface { + GetDB(ctx context.Context) (*DataDatabasesMap, error) + Run() + Stop() + Ping() error +} diff --git a/reader/model/jsonSpan.go b/reader/model/jsonSpan.go new file mode 100644 index 00000000..50893144 --- /dev/null +++ b/reader/model/jsonSpan.go @@ -0,0 +1,30 @@ +package model + +import v1 "go.opentelemetry.io/proto/otlp/trace/v1" + +type JSONSpan struct { + TraceID string `json:"traceID"` + TraceId string `json:"traceId"` + SpanID string `json:"spanID"` + SpanId string `json:"spanId"` + Name string `json:"name"` + StartTimeUnixNano uint64 `json:"startTimeUnixNano"` + EndTimeUnixNano uint64 `json:"endTimeUnixNano"` + ParentSpanId string `json:"parentSpanId,omitempty"` + ServiceName string `json:"serviceName"` + Attributes []JSONSpanAttribute `json:"attributes"` + Events []JSONSpanEvent `json:"events"` + Status *v1.Status `json:"status,omitempty"` +} + +type JSONSpanAttribute struct { + Key string `json:"key"` + Value struct { + StringValue string `json:"stringValue"` + } `json:"value"` +} + +type JSONSpanEvent struct { + TimeUnixNano uint64 `json:"timeUnixNano"` + Name string `json:"name"` +} diff --git a/reader/model/jsonscan.go b/reader/model/jsonscan.go new file mode 100644 index 00000000..51e5576d --- /dev/null +++ b/reader/model/jsonscan.go @@ -0,0 +1,75 @@ +package model + +import ( + "database/sql/driver" + "encoding/json" + "errors" +) + +type JSONText json.RawMessage + +var emptyJSON = JSONText("{}") +var emptyArrayJSON = JSONText("[]") + +func (js JSONText) Value() (driver.Value, error) { + return js.String(), nil +} + +func (js *JSONText) Scan(value interface{}) error { + // if value is nil, false + if value == nil { + // set the value of the pointer yne to JSONText(false) + *js = emptyJSON + return nil + } + var source []byte + + switch t := value.(type) { + case string: + source = []byte(t) + case []byte: + if len(t) == 0 { + source = emptyJSON + } else { + source = t + } + case nil: + *js = emptyJSON + default: + return errors.New("Incompatible type for JSONText") + } + + *js = JSONText(append((*js)[0:0], source...)) + return nil + +} + +// MarshalJSON returns the *j as the JSON encoding of j. +func (j JSONText) MarshalJSON() ([]byte, error) { + if len(j) == 0 { + return emptyJSON, nil + } + return j, nil +} + +// UnmarshalJSON sets *j to a copy of data +func (j *JSONText) UnmarshalJSON(data []byte) error { + if j == nil { + return errors.New("JSONText: UnmarshalJSON on nil pointer") + } + *j = append((*j)[0:0], data...) + return nil +} + +// Unmarshal unmarshal's the json in j to v, as in json.Unmarshal. +func (j *JSONText) Unmarshal(v interface{}) error { + if len(*j) == 0 { + *j = emptyJSON + } + return json.Unmarshal([]byte(*j), v) +} + +// String supports pretty printing for JSONText types. +func (j JSONText) String() string { + return string(j) +} diff --git a/reader/model/metricdata.go b/reader/model/metricdata.go new file mode 100644 index 00000000..2e8580c7 --- /dev/null +++ b/reader/model/metricdata.go @@ -0,0 +1,28 @@ +package model + +import ( + "time" +) + +// Data +type PrometheusMetric struct { + Version uint32 `protobuf:"varint,1,req,name=Version" json:"Version"` + Protocol uint32 `protobuf:"varint,2,req,name=Protocol" json:"Protocol"` + SrcIP string `protobuf:"bytes,3,req,name=SrcIP" json:"SrcIP"` + DstIP string `protobuf:"bytes,4,req,name=DstIP" json:"DstIP"` + SrcPort uint32 `protobuf:"varint,5,req,name=SrcPort" json:"SrcPort"` + DstPort uint32 `protobuf:"varint,6,req,name=DstPort" json:"DstPort"` + Tsec uint32 `protobuf:"varint,7,req,name=Tsec" json:"Tsec"` + Tmsec uint32 `protobuf:"varint,8,req,name=Tmsec" json:"Tmsec"` + ProtoType uint32 `protobuf:"varint,9,req,name=ProtoType" json:"ProtoType"` + NodeID uint32 `protobuf:"varint,10,req,name=NodeID" json:"NodeID"` + NodePW string `protobuf:"bytes,11,req,name=NodePW" json:"NodePW"` + Payload string `protobuf:"bytes,12,req,name=Payload" json:"Payload"` + CID string `protobuf:"bytes,13,req,name=CID" json:"CID"` + Vlan uint32 `protobuf:"varint,14,req,name=Vlan" json:"Vlan"` + ProtoString string + Timestamp time.Time + NodeName string + TargetName string + SID string +} diff --git a/reader/model/prometheus.go b/reader/model/prometheus.go new file mode 100644 index 00000000..e4443bc9 --- /dev/null +++ b/reader/model/prometheus.go @@ -0,0 +1,102 @@ +package model + +import ( + "github.com/prometheus/prometheus/model/labels" + "github.com/prometheus/prometheus/storage" + "github.com/prometheus/prometheus/tsdb/chunkenc" +) + +type ILabelsGetter interface { + Get(fp uint64) labels.Labels +} + +type SeriesSet struct { + Error error + Series []*Series + idx int +} + +func (e *SeriesSet) Reset() { + e.idx = -1 +} + +func (e *SeriesSet) Err() error { + return e.Error +} + +func (e *SeriesSet) Next() bool { + e.idx++ + return e.Series != nil && e.idx < len(e.Series) +} + +func (e *SeriesSet) At() storage.Series { + return e.Series[e.idx] +} + +func (e *SeriesSet) Warnings() storage.Warnings { + return nil +} + +type Sample struct { + TimestampMs int64 + Value float64 +} + +type Series struct { + LabelsGetter ILabelsGetter + Fp uint64 + Samples []Sample +} + +func (s *Series) Labels() labels.Labels { + return s.LabelsGetter.Get(s.Fp) +} + +func (s *Series) Iterator() chunkenc.Iterator { + return &seriesIt{ + samples: s.Samples, + idx: -1, + } +} + +type seriesIt struct { + samples []Sample + idx int +} + +func (s *seriesIt) Next() bool { + s.idx++ + return s.idx < len(s.samples) +} + +func (s *seriesIt) Seek(t int64) bool { + l := 0 + u := len(s.samples) + idx := int(0) + if t <= s.samples[0].TimestampMs { + s.idx = 0 + return true + } + for u > l { + idx = (u + l) / 2 + if s.samples[idx].TimestampMs == t { + l = idx + break + } + if s.samples[idx].TimestampMs < t { + l = idx + 1 + continue + } + u = idx + } + s.idx = idx + return s.idx < len(s.samples) +} + +func (s *seriesIt) At() (int64, float64) { + return s.samples[s.idx].TimestampMs, s.samples[s.idx].Value +} + +func (s *seriesIt) Err() error { + return nil +} diff --git a/reader/model/responses.go b/reader/model/responses.go new file mode 100644 index 00000000..17d4fd36 --- /dev/null +++ b/reader/model/responses.go @@ -0,0 +1,63 @@ +package model + +import ( + v1 "go.opentelemetry.io/proto/otlp/trace/v1" +) + +type SpanResponse struct { + Span *v1.Span + ServiceName string +} + +type TraceResponse struct { + TraceID string `json:"traceID"` + RootServiceName string `json:"rootServiceName"` + RootTraceName string `json:"rootTraceName"` + StartTimeUnixNano int64 `json:"startTimeUnixNano"` + DurationMs int64 `json:"durationMs"` +} + +type TSDBStatus struct { + TotalSeries int32 `json:"totalSeries"` + TotalLabelValuePairs int32 `json:"totalLabelValuePairs"` + SeriesCountByMetricName []TSDBStatusMetric `json:"seriesCountByMetricName"` + SeriesCountByLabelName []TSDBStatusMetric `json:"seriesCountByLabelName"` + SeriesCountByFocusLabelValue []TSDBStatusMetric `json:"seriesCountByFocusLabelValue"` + SeriesCountByLabelValuePair []TSDBStatusMetric `json:"seriesCountByLabelValuePair"` + LabelValueCountByLabelName []TSDBStatusMetric `json:"labelValueCountByLabelName"` + Quota int32 `json:"quota"` +} + +type TSDBStatusMetric struct { + Name string `json:"name"` + Value int32 `json:"value"` +} + +type TraceInfo struct { + TraceID string `json:"traceID"` + RootServiceName string `json:"rootServiceName"` + RootTraceName string `json:"rootTraceName"` + StartTimeUnixNano string `json:"startTimeUnixNano"` + DurationMs float64 `json:"durationMs"` + SpanSet SpanSet `json:"spanSet"` + SpanSets []SpanSet `json:"spanSets"` +} + +type SpanInfo struct { + SpanID string `json:"spanID"` + StartTimeUnixNano string `json:"startTimeUnixNano"` + DurationNanos string `json:"durationNanos"` + Attributes []SpanAttr `json:"attributes"` +} + +type SpanSet struct { + Spans []SpanInfo `json:"spans"` + Matched int `json:"matched"` +} + +type SpanAttr struct { + Key string `json:"key"` + Value struct { + StringValue string `json:"stringValue"` + } `json:"value"` +} diff --git a/reader/model/sampleModel.go b/reader/model/sampleModel.go new file mode 100755 index 00000000..6d9c8eba --- /dev/null +++ b/reader/model/sampleModel.go @@ -0,0 +1,37 @@ +package model + +func (TableSample) TableName() string { + return "samples" +} + +func (TableSample) TableEngine() string { + return "MergeTree PARTITION BY toDate(timestamp_ms / 1000) ORDER BY (fingerprint, timestamp_ms);" +} + +// swagger:model CreateUserStruct +type TableSample struct { + FingerPrint uint64 `db:"fingerprint" clickhouse:"type:UInt64" json:"fingerprint"` + // required: true + TimestampMS int64 `db:"timestamp_ms" clickhouse:"type:Int64" json:"timestamp_ms"` + // + Value float64 `db:"value" clickhouse:"type:Float64" json:"value"` + // example: 10 + // required: true + String string `db:"string" clickhouse:"type:String" json:"string"` +} + +/* +CREATE TABLE cloki.samples +( + `fingerprint` UInt64, + `timestamp_ms` Int64, + `value` Float64, + `string` String +) +ENGINE = MergeTree +PARTITION BY toRelativeHourNum(toDateTime(timestamp_ms / 1000)) +ORDER BY (fingerprint, timestamp_ms) +TTL toDateTime(timestamp_ms / 1000) + toIntervalDay(7) +SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1, merge_with_ttl_timeout = 3600 + +*/ diff --git a/reader/model/services.go b/reader/model/services.go new file mode 100644 index 00000000..2f814388 --- /dev/null +++ b/reader/model/services.go @@ -0,0 +1,56 @@ +package model + +import ( + "context" + "github.com/jmoiron/sqlx" + "time" +) + +type ITempoService interface { + Query(ctx context.Context, startNS int64, endNS int64, traceId []byte, binIds bool) (chan *SpanResponse, error) + Tags(ctx context.Context) (chan string, error) + Values(ctx context.Context, tag string) (chan string, error) + ValuesV2(ctx context.Context, key string, query string, from time.Time, to time.Time, limit int) (chan string, error) + Search(ctx context.Context, tags string, minDurationNS int64, maxDurationNS int64, + limit int, fromNS int64, toNS int64) (chan *TraceResponse, error) + SearchTraceQL(ctx context.Context, q string, limit int, from time.Time, to time.Time) (chan []TraceInfo, error) + TagsV2(ctx context.Context, query string, from time.Time, to time.Time, limit int) (chan string, error) +} + +type IQueryLabelsService interface { + Labels(ctx context.Context, startMs int64, endMs int64, labelsType uint16) (chan string, error) + PromValues(ctx context.Context, label string, match []string, startMs int64, endMs int64, + labelsType uint16) (chan string, error) + Prom2LogqlMatch(match string) (string, error) + Values(ctx context.Context, label string, match []string, startMs int64, endMs int64, + labelsType uint16) (chan string, error) + Series(ctx context.Context, requests []string, startMs int64, endMs int64, + labelsType uint16) (chan string, error) +} +type IQueryRangeService interface { + QueryRange(ctx context.Context, query string, fromNs int64, toNs int64, stepMs int64, + limit int64, forward bool) (chan QueryRangeOutput, error) + QueryInstant(ctx context.Context, query string, timeNs int64, stepMs int64, + limit int64) (chan QueryRangeOutput, error) + Tail(ctx context.Context, query string) (IWatcher, error) +} + +// Service : here you tell us what Salutation is +type ServiceData struct { + Session IDBRegistry + lastPingTime time.Time +} + +func (s *ServiceData) Ping() error { + return s.Session.Ping() +} + +// ServiceConfig +type ServiceConfig struct { + Session *sqlx.DB +} + +// ServiceConfigDatabases +type ServiceConfigDatabases struct { + Session map[string]*sqlx.DB +} diff --git a/reader/model/streams.go b/reader/model/streams.go new file mode 100755 index 00000000..1faaeef9 --- /dev/null +++ b/reader/model/streams.go @@ -0,0 +1,25 @@ +package model + +import "time" + +type PushRequest struct { + Streams []Stream `json:"streams"` +} +type Stream struct { + Labels string `json:"labels"` + Entries []Entry `json:"entries"` +} + +// Entry is a log entry with a timestamp. +type Entry struct { + Timestamp time.Time `json:"timestamp"` + Line string `json:"line"` +} + +type Label struct { + Key, Value string +} + +type LabelRules struct { + Label, Cond, Value string +} diff --git a/reader/model/timeSeries.go b/reader/model/timeSeries.go new file mode 100755 index 00000000..2a92712a --- /dev/null +++ b/reader/model/timeSeries.go @@ -0,0 +1,37 @@ +package model + +import "time" + +func (TableTimeSeries) TableName() string { + return "time_series" +} + +func (TableTimeSeries) TableEngine() string { + return "ReplacingMergeTree PARTITION BY date ORDER BY fingerprint;" +} + +type TableTimeSeries struct { + Date time.Time `db:"date" clickhouse:"type:Date" json:"date"` + // required: true + FingerPrint uint64 `db:"fingerprint" clickhouse:"type:UInt64" json:"fingerprint"` + // + Labels string `db:"labels" clickhouse:"type:String" json:"value"` + // example: 10 + // required: true + Name string `db:"name" clickhouse:"type:String" json:"string"` +} + +/* +CREATE TABLE cloki.time_series +( + `date` Date, + `fingerprint` UInt64, + `labels` String, + `name` String +) +ENGINE = ReplacingMergeTree(date) +PARTITION BY date +ORDER BY fingerprint +TTL date + toIntervalDay(7) +SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1, merge_with_ttl_timeout = 3600 +*/ diff --git a/reader/model/userModel.go b/reader/model/userModel.go new file mode 100644 index 00000000..beb144ca --- /dev/null +++ b/reader/model/userModel.go @@ -0,0 +1,254 @@ +package model + +import ( + "encoding/json" + "os" + "time" +) + +var ProxyTokens = make(map[string]int64) + +func (TableUser) TableName() string { + return "users" +} + +func (TableUser) TableEngine() string { + return "ReplacingMergeTree" +} + +// swagger:model CreateUserStruct +type TableUser struct { + UUID string `db:"uuid" csv:"-" clickhouse:"type:UUID;default:generateUUIDv4()" json:"guid"` + // required: true + Version uint64 `db:"version" csv:"-" clickhouse:"type:UInt64;default:NOW();key" json:"version" validate:"required,gte=1"` + // + UserName string `db:"username" csv:"username" clickhouse:"type:String;order" json:"username" validate:"required,username"` + // example: 10 + // required: true + PartID uint16 `db:"partid" csv:"partid" clickhouse:"type:UInt16;default:10" json:"partid" validate:"required,gte=1"` + // required: true + Email string `db:"email" csv:"email" clickhouse:"type:String" json:"email" validate:"required,email"` + // required: true + Password string `db:"-" csv:"password" json:"password"` + // required: true + FirstName string `db:"firstname" csv:"firstname" clickhouse:"type:String" json:"firstname" validate:"required,min=2,ascii"` + // required: true + LastName string `db:"lastname" csv:"lastname" clickhouse:"type:String" json:"lastname"` + // required: true + // example: NOC + Department string `db:"department" csv:"department" clickhouse:"type:String" json:"department"` + // required: true + // example: admin + UserGroup string `db:"usergroup" csv:"usergroup" clickhouse:"type:String" json:"usergroup" validate:"required,alphanum"` + IsAdmin bool `db:"-" csv:"-" json:"-"` + ExternalAuth bool `db:"-" csv:"-" json:"-"` + ForcePassword bool `db:"-" csv:"-" json:"-"` + + Params JSONText `db:"params" csv:"params" clickhouse:"type:String" json:"params"` + + Hash string `db:"hash" csv:"passwordhash" clickhouse:"type:String" json:"-"` + + // required: true + CreatedAt time.Time `db:"record_datetime" csv:"-" clickhouse:"type:DateTime;default:NOW()" json:"-"` + + ExternalProfile string `db:"-" json:"-"` + + Avatar string `db:"-" json:"-"` +} + +// swagger:model UserLegacyStruct +type TableUserLegacyFormat struct { + UserName string `csv:"username" validate:"alphanum"` + // required: true + PartID uint16 `csv:"partid" validate:"required,gte=1"` + // required: true + Email string `csv:"email" validate:"required,email"` + // required: true + Password string `csv:"password"` + // required: true + FirstName string `csv:"firstname" validate:"required,alphanum"` + // required: true + LastName string `csv:"lastname" validate:"required,alphanum"` + // required: true + // example: NOC + Department string `csv:"department"` + // example: admin + UserGroup string `csv:"usergroup" validate:"required,alphanum"` + //example {} + Params string `csv:"params"` + // example: admin + PasswordHash string `csv:"passwordhash"` +} + +// swagger:model UserLoginSuccessResponse +type UserTokenSuccessfulResponse struct { + // the token + // example: JWT Token + Token string `json:"token"` + // the uuid + // example: b9f6q23a-0bde-41ce-cd36-da3dbc17ea12 + Scope string `json:"scope"` + User struct { + Admin bool `json:"admin"` + ForcePassword bool `json:"force_password"` + } `json:"user"` +} + +// swagger:model UserDetailsResponse +type UserDetailsResponse struct { + // the uuid + User struct { + Admin bool `json:"admin"` + Username string `json:"username"` + Usergroup string `json:"usergroup"` + ForcePassword bool `json:"force_password"` + } `json:"user"` +} + +// swagger:model FailureResponse +type UserTokenBadResponse struct { + // statuscode + StatusCode int `json:"statuscode"` + // errot + Error string `json:"error"` + // message + Message string `json:"message"` +} + +// swagger:model ListUsers +type GetUser struct { + // count + Count int `json:"count"` + // the data + Data []*TableUser `json:"data"` +} + +// swagger:model UserLogin +type UserloginDetails struct { + // example: admin + // required: true + Username string `json:"username" validate:"required"` + // example: sipcapture + // required: true + Password string `json:"password" validate:"required"` + // the type of the auth one would like to perform, internal/ldap + // example: internal + // required: false + Type string `json:"type" validate:"-"` +} + +// swagger:model UserSuccessResponse +type UserCreateSuccessfulResponse struct { + // data in JSON format + // + // required: true + // + // example: af72057b-2745-0a1b-b674-56586aadec57 + Data string `json:"data"` + // the message for user + // + // required: true + // example: successfully created user + Message string `json:"message"` +} + +// swagger:model UserUpdateSuccessResponse +type UserUpdateSuccessfulResponse struct { + // example: af72057b-2745-0a1b-b674-56586aadec57 + Data string `json:"data"` + // example: successfully updated user + Message string `json:"message"` +} + +// swagger:model UserDeleteSuccessResponse +type UserDeleteSuccessfulResponse struct { + // example: af72057b-2745-0a1b-b674-56586aadec57 + Data string `json:"data"` + // example: successfully deleted user + Message string `json:"message"` +} + +type HTTPAUTHResp struct { + Auth bool `json:"auth" validate:"required"` + Data TableUser `json:"data" validate:"required"` +} + +// swagger:model UserLoginSuccessResponse +type UserProxyTokenData struct { + // the token + Token string `json:"token"` + // required: true + ExpireAt time.Time `json:"expire_at"` +} + +// swagger:model CreateUserStruct +type TableUserPasswordUpdate struct { + UUID string `db:"-" csv:"-" json:"guid"` + // required: true + Password string `db:"-" csv:"password" json:"password"` + // required: true + OldPassword string `db:"-" csv:"old_password" json:"old_password"` +} + +// swagger:model CreateUserStruct +type UserObject struct { + UserName string `json:"username"` + // example: 10 + // required: true + PartID uint16 `json:"partid"` + // required: true + UserGroup string `json:"usergroup"` +} + +// swagger:model UserFileUpload +type UserFileUpload struct { + // in: formData + // swagger:file + File os.File +} + +// swagger:model UserFileDownload +type UserFileDownload struct { + // in: body + // swagger:file + File os.File +} + +// swagger:parameters UserFileResponse UserFileRequest +type UserParameterRequest struct { + // in: formData + // swagger:file + File interface{} +} + +//swagger:model TableUserList +type TableUserList struct { + Data []TableUser `json:"data"` + // example: 13 + Count int `json:"count"` +} + +//swagger:model UserGroupList +type UserGroupList struct { + // example: ["admin","user"] + Data []string `json:"data"` + // example: 13 + Count int `json:"count"` +} + +// swagger:model OAuth2TokenExchange +type OAuth2TokenExchange struct { + // example: token + // required: true + OneTimeToken string `json:"token" validate:"required"` +} + +// swagger:model OAuth2MapToken +type OAuth2MapToken struct { + AccessToken string `json:"access_token"` + Provider string `json:"provider"` + DataJson json.RawMessage `json:"datajson"` + CreateDate time.Time `json:"create_date"` + ExpireDate time.Time `json:"expire_date"` + ProfileJson json.RawMessage `json:"profile_json"` +} diff --git a/reader/model/watcher.go b/reader/model/watcher.go new file mode 100644 index 00000000..8fa9e6f1 --- /dev/null +++ b/reader/model/watcher.go @@ -0,0 +1,12 @@ +package model + +type QueryRangeOutput struct { + Str string + Err error +} + +type IWatcher interface { + Close() + GetRes() chan QueryRangeOutput + Done() <-chan struct{} +} diff --git a/reader/plugins/logs_planners.go b/reader/plugins/logs_planners.go new file mode 100644 index 00000000..8d30477d --- /dev/null +++ b/reader/plugins/logs_planners.go @@ -0,0 +1,78 @@ +package plugins + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "time" +) + +type SeriesPlannerPlugin func(main shared.SQLRequestPlanner) shared.SQLRequestPlanner + +var seriesPlannerPlugin *SeriesPlannerPlugin + +func RegisterSeriesPlannerPlugin(plugin SeriesPlannerPlugin) { + seriesPlannerPlugin = &plugin +} + +func GetSeriesPlannerPlugin() *SeriesPlannerPlugin { + return seriesPlannerPlugin +} + +type Metrics15ShortcutPlannerPlugin func(fn string, duration time.Duration) shared.SQLRequestPlanner + +var metrics15ShortcutPlannerPlugin *Metrics15ShortcutPlannerPlugin + +func RegisterMetrics15ShortcutPlannerPlugin(plugin Metrics15ShortcutPlannerPlugin) { + metrics15ShortcutPlannerPlugin = &plugin +} + +func GetMetrics15ShortcutPlannerPlugin() *Metrics15ShortcutPlannerPlugin { + return metrics15ShortcutPlannerPlugin +} + +type TimeSeriesInitPlannerPlugin func() shared.SQLRequestPlanner + +var timeSeriesInitPlannerPlugin *TimeSeriesInitPlannerPlugin + +func RegisterTimeSeriesInitPlannerPlugin(plugin TimeSeriesInitPlannerPlugin) { + timeSeriesInitPlannerPlugin = &plugin +} + +func GetTimeSeriesInitPlannerPlugin() *TimeSeriesInitPlannerPlugin { + return timeSeriesInitPlannerPlugin +} + +type SqlMainInitPlannerPlugin func() shared.SQLRequestPlanner + +var sqlMainInitPlannerPlugin *SqlMainInitPlannerPlugin + +func RegisterSqlMainInitPlannerPlugin(plugin SqlMainInitPlannerPlugin) { + sqlMainInitPlannerPlugin = &plugin +} + +func GetSqlMainInitPlannerPlugin() *SqlMainInitPlannerPlugin { + return sqlMainInitPlannerPlugin +} + +type ValuesPlannerPlugin func(main shared.SQLRequestPlanner, key string) shared.SQLRequestPlanner + +var valuesPlannerPlugin *ValuesPlannerPlugin + +func RegisterValuesPlannerPlugin(plugin ValuesPlannerPlugin) { + valuesPlannerPlugin = &plugin +} + +func GetValuesPlannerPlugin() *ValuesPlannerPlugin { + return valuesPlannerPlugin +} + +type StreamSelectPlannerPlugin func(LabelNames []string, ops []string, values []string) shared.SQLRequestPlanner + +var streamSelectPlannerPlugin *StreamSelectPlannerPlugin + +func RegisterStreamSelectPlannerPlugin(plugin StreamSelectPlannerPlugin) { + streamSelectPlannerPlugin = &plugin +} + +func GetStreamSelectPlannerPlugin() *StreamSelectPlannerPlugin { + return streamSelectPlannerPlugin +} diff --git a/reader/plugins/metrics_planners.go b/reader/plugins/metrics_planners.go new file mode 100644 index 00000000..d907d201 --- /dev/null +++ b/reader/plugins/metrics_planners.go @@ -0,0 +1,27 @@ +package plugins + +import "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + +type InitClickhousePlannerPlugin func() shared.SQLRequestPlanner + +var initClickhousePlannerPlugin *InitClickhousePlannerPlugin + +func RegisterInitClickhousePlannerPlugin(plugin InitClickhousePlannerPlugin) { + initClickhousePlannerPlugin = &plugin +} + +func GetInitClickhousePlannerPlugin() *InitClickhousePlannerPlugin { + return initClickhousePlannerPlugin +} + +type InitDownsamplePlannerPlugin func() shared.SQLRequestPlanner + +var initDownsamplePlannerPlugin *InitDownsamplePlannerPlugin + +func RegisterInitDownsamplePlannerPlugin(plugin InitDownsamplePlannerPlugin) { + initDownsamplePlannerPlugin = &plugin +} + +func GetInitDownsamplePlannerPlugin() *InitDownsamplePlannerPlugin { + return initDownsamplePlannerPlugin +} diff --git a/reader/plugins/plugins.go b/reader/plugins/plugins.go new file mode 100644 index 00000000..4978cbff --- /dev/null +++ b/reader/plugins/plugins.go @@ -0,0 +1,62 @@ +package plugins + +import ( + "context" + "errors" + "github.com/metrico/qryn/reader/logql/logql_parser" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/model" + "net/http" +) + +var ErrPluginNotApplicable = errors.New("plugin not applicable") + +type LogQLTranspilerPlugin interface { + Plan(script *logql_parser.LogQLScript) (shared.RequestProcessorChain, error) +} + +var logQLTranspilerPlugins []LogQLTranspilerPlugin + +func RegisterLogQLPlannerPlugin(name string, plugin LogQLTranspilerPlugin) { + logQLTranspilerPlugins = append(logQLTranspilerPlugins, plugin) +} + +func GetLogQLPlannerPlugins() []LogQLTranspilerPlugin { + return logQLTranspilerPlugins +} + +type PreRequestPlugin func(ctx context.Context, req *http.Request) (context.Context, error) + +var preRequestPlugins []PreRequestPlugin + +func RegisterPreRequestPlugin(name string, plugin PreRequestPlugin) { + preRequestPlugins = append(preRequestPlugins, plugin) +} + +func GetPreRequestPlugins() []PreRequestPlugin { + return preRequestPlugins +} + +type PreWSRequestPlugin func(ctx context.Context, req *http.Request) (context.Context, error) + +var preWSRequestPlugins []PreWSRequestPlugin + +func RegisterPreWSRequestPlugin(name string, plugin PreWSRequestPlugin) { + preWSRequestPlugins = append(preWSRequestPlugins, plugin) +} + +func GetPreWSRequestPlugins() []PreWSRequestPlugin { + return preWSRequestPlugins +} + +type DatabaseRegistryPlugin func() model.IDBRegistry + +var databaseRegistryPlugin *DatabaseRegistryPlugin + +func RegisterDatabaseRegistryPlugin(plugin DatabaseRegistryPlugin) { + databaseRegistryPlugin = &plugin +} + +func GetDatabaseRegistryPlugin() *DatabaseRegistryPlugin { + return databaseRegistryPlugin +} diff --git a/reader/plugins/router_plugins.go b/reader/plugins/router_plugins.go new file mode 100644 index 00000000..b3ee978e --- /dev/null +++ b/reader/plugins/router_plugins.go @@ -0,0 +1,30 @@ +package plugins + +import ( + "github.com/gorilla/mux" + "github.com/metrico/qryn/reader/model" + "github.com/prometheus/prometheus/storage" +) + +type Services struct { + TempoService model.ITempoService + QueryLabelsService model.IQueryLabelsService + PrometheusService storage.Queryable + QueryRangeService model.IQueryRangeService + ServiceData model.ServiceData +} + +type IRoutePlugin interface { + Route(router *mux.Router) + SetServices(services Services) +} + +var routePlugins []IRoutePlugin + +func RegisterRoutePlugin(name string, p IRoutePlugin) { + routePlugins = append(routePlugins, p) +} + +func GetRoutePlugins() []IRoutePlugin { + return routePlugins +} diff --git a/reader/plugins/service_plugins.go b/reader/plugins/service_plugins.go new file mode 100644 index 00000000..895020db --- /dev/null +++ b/reader/plugins/service_plugins.go @@ -0,0 +1,55 @@ +package plugins + +import ( + "context" + "github.com/metrico/qryn/reader/model" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type TempoServicePlugin interface { + GetQueryRequest(ctx context.Context, startNS int64, endNS int64, traceId []byte, + conn *model.DataDatabasesMap) sql.ISelect + GetTagsQuery(ctx context.Context, conn *model.DataDatabasesMap) sql.ISelect + GetValuesQuery(ctx context.Context, tag string, conn *model.DataDatabasesMap) sql.ISelect +} + +var tempoServicePlugin *TempoServicePlugin + +func RegisterTempoServicePlugin(factory TempoServicePlugin) { + tempoServicePlugin = &factory +} + +func GetTempoServicePlugin() *TempoServicePlugin { + return tempoServicePlugin +} + +type QueryLabelsServicePlugin interface { + SetServiceData(data *model.ServiceData) + EstimateKVComplexity(ctx context.Context, conn *model.DataDatabasesMap) sql.ISelect + Labels(ctx context.Context, startMs int64, endMs int64, labelsType uint16) (chan string, error) +} + +var queryLabelsServicePlugin *QueryLabelsServicePlugin + +func RegisterQueryLabelsServicePlugin(plugin QueryLabelsServicePlugin) { + queryLabelsServicePlugin = &plugin +} + +func GetQueryLabelsServicePlugin() *QueryLabelsServicePlugin { + return queryLabelsServicePlugin +} + +type QueryRangeServicePlugin interface { + SetServiceData(data *model.ServiceData) + Tail(ctx context.Context, query string) (model.IWatcher, error) +} + +var queryRangeServicePlugin *QueryRangeServicePlugin + +func RegisterQueryRangeServicePlugin(factory QueryRangeServicePlugin) { + queryRangeServicePlugin = &factory +} + +func GetQueryRangeServicePlugin() *QueryRangeServicePlugin { + return queryRangeServicePlugin +} diff --git a/reader/plugins/tables_plugins.go b/reader/plugins/tables_plugins.go new file mode 100644 index 00000000..f94e1be0 --- /dev/null +++ b/reader/plugins/tables_plugins.go @@ -0,0 +1,17 @@ +package plugins + +const ( + tableNamesPluginsPefix = "table-names-plugin-" +) + +type TableNamesPlugin func() map[string]string + +var tableNamesPlugin *TableNamesPlugin + +func RegisterTableNamesPlugin(name string, plugin TableNamesPlugin) { + tableNamesPlugin = &plugin +} + +func GetTableNamesPlugin() *TableNamesPlugin { + return tableNamesPlugin +} diff --git a/reader/plugins/traces_planners.go b/reader/plugins/traces_planners.go new file mode 100644 index 00000000..99332eb9 --- /dev/null +++ b/reader/plugins/traces_planners.go @@ -0,0 +1,81 @@ +package plugins + +import ( + "context" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/model" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "time" +) + +const ( + tracesDataPluginName = "traces-data-plugin" + attrlessConditionPlannerPluginName = "attrless-condition-planner" + getTracesQueryPluginName = "get-traces-query-plugin" + labelsGetterPluginName = "labels-getter-plugin" + initIndexPlannerPluginName = "init-index-planner" +) + +type TracesDataPlugin func(main shared.SQLRequestPlanner) shared.SQLRequestPlanner + +var traceDataPlugin *TracesDataPlugin + +func RegisterTracesDataPlugin(plugin TracesDataPlugin) { + traceDataPlugin = &plugin +} + +func GetTracesDataPlugin() *TracesDataPlugin { + return traceDataPlugin +} + +type AttrlessConditionPlannerPlugin func() shared.SQLRequestPlanner + +var attrlessConditionPlannerPlugin *AttrlessConditionPlannerPlugin + +func RegisterAttrlessConditionPlannerPlugin(plugin AttrlessConditionPlannerPlugin) { + attrlessConditionPlannerPlugin = &plugin +} + +func GetAttrlessConditionPlannerPlugin() *AttrlessConditionPlannerPlugin { + return attrlessConditionPlannerPlugin +} + +type GetTracesQueryPlugin func(ctx context.Context, idx any, limit int, fromNS int64, toNS int64, + distributed bool, minDurationNS int64, maxDurationNS int64) (sql.ISelect, error) + +var getTracesQueryPlugin *GetTracesQueryPlugin + +func RegisterGetTracesQueryPlugin(plugin GetTracesQueryPlugin) { + getTracesQueryPlugin = &plugin +} + +func GetGetTracesQueryPlugin() *GetTracesQueryPlugin { + return getTracesQueryPlugin +} + +type LabelsGetterPlugin interface { + GetLabelsQuery(ctx context.Context, conn *model.DataDatabasesMap, + fingerprints map[uint64]bool, from time.Time, to time.Time) sql.ISelect +} + +var labelsGetterPlugin *LabelsGetterPlugin + +func RegisterLabelsGetterPlugin(plugin LabelsGetterPlugin) { + labelsGetterPlugin = &plugin +} + +func GetLabelsGetterPlugin() *LabelsGetterPlugin { + return labelsGetterPlugin +} + +type InitIndexPlannerPlugin func() shared.SQLRequestPlanner + +var initIndexPlannerPlugin *InitIndexPlannerPlugin + +func RegisterInitIndexPlannerPlugin(plugin InitIndexPlannerPlugin) { + initIndexPlannerPlugin = &plugin +} + +func GetInitIndexPlannerPlugin() *InitIndexPlannerPlugin { + return initIndexPlannerPlugin +} diff --git a/reader/prof/google/v1/profile.pb.go b/reader/prof/google/v1/profile.pb.go new file mode 100644 index 00000000..1e077bcc --- /dev/null +++ b/reader/prof/google/v1/profile.pb.go @@ -0,0 +1,988 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Profile is a common stacktrace profile format. +// +// Measurements represented with this format should follow the +// following conventions: +// +// - Consumers should treat unset optional fields as if they had been +// set with their default value. +// +// - When possible, measurements should be stored in "unsampled" form +// that is most useful to humans. There should be enough +// information present to determine the original sampled values. +// +// - On-disk, the serialized proto must be gzip-compressed. +// +// - The profile is represented as a set of samples, where each sample +// references a sequence of locations, and where each location belongs +// to a mapping. +// - There is a N->1 relationship from sample.location_id entries to +// locations. For every sample.location_id entry there must be a +// unique Location with that id. +// - There is an optional N->1 relationship from locations to +// mappings. For every nonzero Location.mapping_id there must be a +// unique Mapping with that id. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.4 +// protoc v3.21.12 +// source: google/v1/profile.proto + +package v1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Profile struct { + state protoimpl.MessageState `protogen:"open.v1"` + // A description of the samples associated with each Sample.value. + // For a cpu profile this might be: + // + // [["cpu","nanoseconds"]] or [["wall","seconds"]] or [["syscall","count"]] + // + // For a heap profile, this might be: + // + // [["allocations","count"], ["space","bytes"]], + // + // If one of the values represents the number of events represented + // by the sample, by convention it should be at index 0 and use + // sample_type.unit == "count". + SampleType []*ValueType `protobuf:"bytes,1,rep,name=sample_type,json=sampleType,proto3" json:"sample_type,omitempty"` + // The set of samples recorded in this profile. + Sample []*Sample `protobuf:"bytes,2,rep,name=sample,proto3" json:"sample,omitempty"` + // Mapping from address ranges to the image/binary/library mapped + // into that address range. mapping[0] will be the main binary. + Mapping []*Mapping `protobuf:"bytes,3,rep,name=mapping,proto3" json:"mapping,omitempty"` + // Useful program location + Location []*Location `protobuf:"bytes,4,rep,name=location,proto3" json:"location,omitempty"` + // Functions referenced by locations + Function []*Function `protobuf:"bytes,5,rep,name=function,proto3" json:"function,omitempty"` + // A common table for strings referenced by various messages. + // string_table[0] must always be "". + StringTable []string `protobuf:"bytes,6,rep,name=string_table,json=stringTable,proto3" json:"string_table,omitempty"` + // frames with Function.function_name fully matching the following + // regexp will be dropped from the samples, along with their successors. + DropFrames int64 `protobuf:"varint,7,opt,name=drop_frames,json=dropFrames,proto3" json:"drop_frames,omitempty"` // Index into string table. + // frames with Function.function_name fully matching the following + // regexp will be kept, even if it matches drop_frames. + KeepFrames int64 `protobuf:"varint,8,opt,name=keep_frames,json=keepFrames,proto3" json:"keep_frames,omitempty"` // Index into string table. + // Time of collection (UTC) represented as nanoseconds past the epoch. + TimeNanos int64 `protobuf:"varint,9,opt,name=time_nanos,json=timeNanos,proto3" json:"time_nanos,omitempty"` + // Duration of the profile, if a duration makes sense. + DurationNanos int64 `protobuf:"varint,10,opt,name=duration_nanos,json=durationNanos,proto3" json:"duration_nanos,omitempty"` + // The kind of events between sampled ocurrences. + // e.g [ "cpu","cycles" ] or [ "heap","bytes" ] + PeriodType *ValueType `protobuf:"bytes,11,opt,name=period_type,json=periodType,proto3" json:"period_type,omitempty"` + // The number of events between sampled occurrences. + Period int64 `protobuf:"varint,12,opt,name=period,proto3" json:"period,omitempty"` + // Freeform text associated to the profile. + Comment []int64 `protobuf:"varint,13,rep,packed,name=comment,proto3" json:"comment,omitempty"` // Indices into string table. + // Index into the string table of the type of the preferred sample + // value. If unset, clients should default to the last sample value. + DefaultSampleType int64 `protobuf:"varint,14,opt,name=default_sample_type,json=defaultSampleType,proto3" json:"default_sample_type,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Profile) Reset() { + *x = Profile{} + mi := &file_google_v1_profile_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Profile) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Profile) ProtoMessage() {} + +func (x *Profile) ProtoReflect() protoreflect.Message { + mi := &file_google_v1_profile_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Profile.ProtoReflect.Descriptor instead. +func (*Profile) Descriptor() ([]byte, []int) { + return file_google_v1_profile_proto_rawDescGZIP(), []int{0} +} + +func (x *Profile) GetSampleType() []*ValueType { + if x != nil { + return x.SampleType + } + return nil +} + +func (x *Profile) GetSample() []*Sample { + if x != nil { + return x.Sample + } + return nil +} + +func (x *Profile) GetMapping() []*Mapping { + if x != nil { + return x.Mapping + } + return nil +} + +func (x *Profile) GetLocation() []*Location { + if x != nil { + return x.Location + } + return nil +} + +func (x *Profile) GetFunction() []*Function { + if x != nil { + return x.Function + } + return nil +} + +func (x *Profile) GetStringTable() []string { + if x != nil { + return x.StringTable + } + return nil +} + +func (x *Profile) GetDropFrames() int64 { + if x != nil { + return x.DropFrames + } + return 0 +} + +func (x *Profile) GetKeepFrames() int64 { + if x != nil { + return x.KeepFrames + } + return 0 +} + +func (x *Profile) GetTimeNanos() int64 { + if x != nil { + return x.TimeNanos + } + return 0 +} + +func (x *Profile) GetDurationNanos() int64 { + if x != nil { + return x.DurationNanos + } + return 0 +} + +func (x *Profile) GetPeriodType() *ValueType { + if x != nil { + return x.PeriodType + } + return nil +} + +func (x *Profile) GetPeriod() int64 { + if x != nil { + return x.Period + } + return 0 +} + +func (x *Profile) GetComment() []int64 { + if x != nil { + return x.Comment + } + return nil +} + +func (x *Profile) GetDefaultSampleType() int64 { + if x != nil { + return x.DefaultSampleType + } + return 0 +} + +// ValueType describes the semantics and measurement units of a value. +type ValueType struct { + state protoimpl.MessageState `protogen:"open.v1"` + Type int64 `protobuf:"varint,1,opt,name=type,proto3" json:"type,omitempty"` // Index into string table. + Unit int64 `protobuf:"varint,2,opt,name=unit,proto3" json:"unit,omitempty"` // Index into string table. + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ValueType) Reset() { + *x = ValueType{} + mi := &file_google_v1_profile_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ValueType) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValueType) ProtoMessage() {} + +func (x *ValueType) ProtoReflect() protoreflect.Message { + mi := &file_google_v1_profile_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValueType.ProtoReflect.Descriptor instead. +func (*ValueType) Descriptor() ([]byte, []int) { + return file_google_v1_profile_proto_rawDescGZIP(), []int{1} +} + +func (x *ValueType) GetType() int64 { + if x != nil { + return x.Type + } + return 0 +} + +func (x *ValueType) GetUnit() int64 { + if x != nil { + return x.Unit + } + return 0 +} + +// Each Sample records values encountered in some program +// context. The program context is typically a stack trace, perhaps +// augmented with auxiliary information like the thread-id, some +// indicator of a higher level request being handled etc. +type Sample struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The ids recorded here correspond to a Profile.location.id. + // The leaf is at location_id[0]. + LocationId []uint64 `protobuf:"varint,1,rep,packed,name=location_id,json=locationId,proto3" json:"location_id,omitempty"` + // The type and unit of each value is defined by the corresponding + // entry in Profile.sample_type. All samples must have the same + // number of values, the same as the length of Profile.sample_type. + // When aggregating multiple samples into a single sample, the + // result has a list of values that is the element-wise sum of the + // lists of the originals. + Value []int64 `protobuf:"varint,2,rep,packed,name=value,proto3" json:"value,omitempty"` + // label includes additional context for this sample. It can include + // things like a thread id, allocation size, etc + Label []*Label `protobuf:"bytes,3,rep,name=label,proto3" json:"label,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Sample) Reset() { + *x = Sample{} + mi := &file_google_v1_profile_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Sample) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Sample) ProtoMessage() {} + +func (x *Sample) ProtoReflect() protoreflect.Message { + mi := &file_google_v1_profile_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Sample.ProtoReflect.Descriptor instead. +func (*Sample) Descriptor() ([]byte, []int) { + return file_google_v1_profile_proto_rawDescGZIP(), []int{2} +} + +func (x *Sample) GetLocationId() []uint64 { + if x != nil { + return x.LocationId + } + return nil +} + +func (x *Sample) GetValue() []int64 { + if x != nil { + return x.Value + } + return nil +} + +func (x *Sample) GetLabel() []*Label { + if x != nil { + return x.Label + } + return nil +} + +type Label struct { + state protoimpl.MessageState `protogen:"open.v1"` + Key int64 `protobuf:"varint,1,opt,name=key,proto3" json:"key,omitempty"` // Index into string table + // At most one of the following must be present + Str int64 `protobuf:"varint,2,opt,name=str,proto3" json:"str,omitempty"` // Index into string table + Num int64 `protobuf:"varint,3,opt,name=num,proto3" json:"num,omitempty"` + // Should only be present when num is present. + // Specifies the units of num. + // Use arbitrary string (for example, "requests") as a custom count unit. + // If no unit is specified, consumer may apply heuristic to deduce the unit. + // Consumers may also interpret units like "bytes" and "kilobytes" as memory + // units and units like "seconds" and "nanoseconds" as time units, + // and apply appropriate unit conversions to these. + NumUnit int64 `protobuf:"varint,4,opt,name=num_unit,json=numUnit,proto3" json:"num_unit,omitempty"` // Index into string table + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Label) Reset() { + *x = Label{} + mi := &file_google_v1_profile_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Label) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Label) ProtoMessage() {} + +func (x *Label) ProtoReflect() protoreflect.Message { + mi := &file_google_v1_profile_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Label.ProtoReflect.Descriptor instead. +func (*Label) Descriptor() ([]byte, []int) { + return file_google_v1_profile_proto_rawDescGZIP(), []int{3} +} + +func (x *Label) GetKey() int64 { + if x != nil { + return x.Key + } + return 0 +} + +func (x *Label) GetStr() int64 { + if x != nil { + return x.Str + } + return 0 +} + +func (x *Label) GetNum() int64 { + if x != nil { + return x.Num + } + return 0 +} + +func (x *Label) GetNumUnit() int64 { + if x != nil { + return x.NumUnit + } + return 0 +} + +type Mapping struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Unique nonzero id for the mapping. + Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + // Address at which the binary (or DLL) is loaded into memory. + MemoryStart uint64 `protobuf:"varint,2,opt,name=memory_start,json=memoryStart,proto3" json:"memory_start,omitempty"` + // The limit of the address range occupied by this mapping. + MemoryLimit uint64 `protobuf:"varint,3,opt,name=memory_limit,json=memoryLimit,proto3" json:"memory_limit,omitempty"` + // Offset in the binary that corresponds to the first mapped address. + FileOffset uint64 `protobuf:"varint,4,opt,name=file_offset,json=fileOffset,proto3" json:"file_offset,omitempty"` + // The object this entry is loaded from. This can be a filename on + // disk for the main binary and shared libraries, or virtual + // abstractions like "[vdso]". + Filename int64 `protobuf:"varint,5,opt,name=filename,proto3" json:"filename,omitempty"` // Index into string table + // A string that uniquely identifies a particular program version + // with high probability. E.g., for binaries generated by GNU tools, + // it could be the contents of the .note.gnu.build-id field. + BuildId int64 `protobuf:"varint,6,opt,name=build_id,json=buildId,proto3" json:"build_id,omitempty"` // Index into string table + // The following fields indicate the resolution of symbolic info. + HasFunctions bool `protobuf:"varint,7,opt,name=has_functions,json=hasFunctions,proto3" json:"has_functions,omitempty"` + HasFilenames bool `protobuf:"varint,8,opt,name=has_filenames,json=hasFilenames,proto3" json:"has_filenames,omitempty"` + HasLineNumbers bool `protobuf:"varint,9,opt,name=has_line_numbers,json=hasLineNumbers,proto3" json:"has_line_numbers,omitempty"` + HasInlineFrames bool `protobuf:"varint,10,opt,name=has_inline_frames,json=hasInlineFrames,proto3" json:"has_inline_frames,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Mapping) Reset() { + *x = Mapping{} + mi := &file_google_v1_profile_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Mapping) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Mapping) ProtoMessage() {} + +func (x *Mapping) ProtoReflect() protoreflect.Message { + mi := &file_google_v1_profile_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Mapping.ProtoReflect.Descriptor instead. +func (*Mapping) Descriptor() ([]byte, []int) { + return file_google_v1_profile_proto_rawDescGZIP(), []int{4} +} + +func (x *Mapping) GetId() uint64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Mapping) GetMemoryStart() uint64 { + if x != nil { + return x.MemoryStart + } + return 0 +} + +func (x *Mapping) GetMemoryLimit() uint64 { + if x != nil { + return x.MemoryLimit + } + return 0 +} + +func (x *Mapping) GetFileOffset() uint64 { + if x != nil { + return x.FileOffset + } + return 0 +} + +func (x *Mapping) GetFilename() int64 { + if x != nil { + return x.Filename + } + return 0 +} + +func (x *Mapping) GetBuildId() int64 { + if x != nil { + return x.BuildId + } + return 0 +} + +func (x *Mapping) GetHasFunctions() bool { + if x != nil { + return x.HasFunctions + } + return false +} + +func (x *Mapping) GetHasFilenames() bool { + if x != nil { + return x.HasFilenames + } + return false +} + +func (x *Mapping) GetHasLineNumbers() bool { + if x != nil { + return x.HasLineNumbers + } + return false +} + +func (x *Mapping) GetHasInlineFrames() bool { + if x != nil { + return x.HasInlineFrames + } + return false +} + +// Describes function and line table debug information. +type Location struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Unique nonzero id for the location. A profile could use + // instruction addresses or any integer sequence as ids. + Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + // The id of the corresponding profile.Mapping for this location. + // It can be unset if the mapping is unknown or not applicable for + // this profile type. + MappingId uint64 `protobuf:"varint,2,opt,name=mapping_id,json=mappingId,proto3" json:"mapping_id,omitempty"` + // The instruction address for this location, if available. It + // should be within [Mapping.memory_start...Mapping.memory_limit] + // for the corresponding mapping. A non-leaf address may be in the + // middle of a call instruction. It is up to display tools to find + // the beginning of the instruction if necessary. + Address uint64 `protobuf:"varint,3,opt,name=address,proto3" json:"address,omitempty"` + // Multiple line indicates this location has inlined functions, + // where the last entry represents the caller into which the + // preceding entries were inlined. + // + // E.g., if memcpy() is inlined into printf: + // + // line[0].function_name == "memcpy" + // line[1].function_name == "printf" + Line []*Line `protobuf:"bytes,4,rep,name=line,proto3" json:"line,omitempty"` + // Provides an indication that multiple symbols map to this location's + // address, for example due to identical code folding by the linker. In that + // case the line information above represents one of the multiple + // symbols. This field must be recomputed when the symbolization state of the + // profile changes. + IsFolded bool `protobuf:"varint,5,opt,name=is_folded,json=isFolded,proto3" json:"is_folded,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Location) Reset() { + *x = Location{} + mi := &file_google_v1_profile_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Location) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Location) ProtoMessage() {} + +func (x *Location) ProtoReflect() protoreflect.Message { + mi := &file_google_v1_profile_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Location.ProtoReflect.Descriptor instead. +func (*Location) Descriptor() ([]byte, []int) { + return file_google_v1_profile_proto_rawDescGZIP(), []int{5} +} + +func (x *Location) GetId() uint64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Location) GetMappingId() uint64 { + if x != nil { + return x.MappingId + } + return 0 +} + +func (x *Location) GetAddress() uint64 { + if x != nil { + return x.Address + } + return 0 +} + +func (x *Location) GetLine() []*Line { + if x != nil { + return x.Line + } + return nil +} + +func (x *Location) GetIsFolded() bool { + if x != nil { + return x.IsFolded + } + return false +} + +type Line struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The id of the corresponding profile.Function for this line. + FunctionId uint64 `protobuf:"varint,1,opt,name=function_id,json=functionId,proto3" json:"function_id,omitempty"` + // Line number in source code. + Line int64 `protobuf:"varint,2,opt,name=line,proto3" json:"line,omitempty"` + // Column number in source code. + Column int64 `protobuf:"varint,3,opt,name=column,proto3" json:"column,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Line) Reset() { + *x = Line{} + mi := &file_google_v1_profile_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Line) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Line) ProtoMessage() {} + +func (x *Line) ProtoReflect() protoreflect.Message { + mi := &file_google_v1_profile_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Line.ProtoReflect.Descriptor instead. +func (*Line) Descriptor() ([]byte, []int) { + return file_google_v1_profile_proto_rawDescGZIP(), []int{6} +} + +func (x *Line) GetFunctionId() uint64 { + if x != nil { + return x.FunctionId + } + return 0 +} + +func (x *Line) GetLine() int64 { + if x != nil { + return x.Line + } + return 0 +} + +func (x *Line) GetColumn() int64 { + if x != nil { + return x.Column + } + return 0 +} + +type Function struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Unique nonzero id for the function. + Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + // Name of the function, in human-readable form if available. + Name int64 `protobuf:"varint,2,opt,name=name,proto3" json:"name,omitempty"` // Index into string table + // Name of the function, as identified by the system. + // For instance, it can be a C++ mangled name. + SystemName int64 `protobuf:"varint,3,opt,name=system_name,json=systemName,proto3" json:"system_name,omitempty"` // Index into string table + // Source file containing the function. + Filename int64 `protobuf:"varint,4,opt,name=filename,proto3" json:"filename,omitempty"` // Index into string table + // Line number in source file. + StartLine int64 `protobuf:"varint,5,opt,name=start_line,json=startLine,proto3" json:"start_line,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Function) Reset() { + *x = Function{} + mi := &file_google_v1_profile_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Function) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Function) ProtoMessage() {} + +func (x *Function) ProtoReflect() protoreflect.Message { + mi := &file_google_v1_profile_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Function.ProtoReflect.Descriptor instead. +func (*Function) Descriptor() ([]byte, []int) { + return file_google_v1_profile_proto_rawDescGZIP(), []int{7} +} + +func (x *Function) GetId() uint64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Function) GetName() int64 { + if x != nil { + return x.Name + } + return 0 +} + +func (x *Function) GetSystemName() int64 { + if x != nil { + return x.SystemName + } + return 0 +} + +func (x *Function) GetFilename() int64 { + if x != nil { + return x.Filename + } + return 0 +} + +func (x *Function) GetStartLine() int64 { + if x != nil { + return x.StartLine + } + return 0 +} + +var File_google_v1_profile_proto protoreflect.FileDescriptor + +var file_google_v1_profile_proto_rawDesc = string([]byte{ + 0x0a, 0x17, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x76, 0x31, 0x22, 0xbf, 0x04, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x12, 0x35, 0x0a, 0x0b, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x73, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x06, 0x73, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x12, 0x2c, 0x0a, 0x07, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, + 0x12, 0x2f, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x2f, 0x0a, 0x08, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x66, 0x72, + 0x61, 0x6d, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x72, 0x6f, 0x70, + 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x66, + 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6b, 0x65, 0x65, + 0x70, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, + 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x35, 0x0a, + 0x0b, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x12, 0x18, 0x0a, 0x07, + 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x03, 0x52, 0x07, 0x63, + 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, + 0x74, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0e, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x11, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x22, 0x33, 0x0a, 0x09, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x6e, 0x69, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x75, 0x6e, 0x69, 0x74, 0x22, 0x67, 0x0a, 0x06, 0x53, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0a, 0x6c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x26, 0x0a, 0x05, + 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x05, 0x6c, + 0x61, 0x62, 0x65, 0x6c, 0x22, 0x58, 0x0a, 0x05, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x73, 0x74, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x73, 0x74, + 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6e, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, + 0x6e, 0x75, 0x6d, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x75, 0x6d, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6e, 0x75, 0x6d, 0x55, 0x6e, 0x69, 0x74, 0x22, 0xd7, + 0x02, 0x0a, 0x07, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, + 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x21, 0x0a, + 0x0c, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, + 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, + 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x07, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x68, 0x61, 0x73, 0x5f, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0c, 0x68, 0x61, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x0a, + 0x0d, 0x68, 0x61, 0x73, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x68, 0x61, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x68, 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x6e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x68, 0x61, + 0x73, 0x4c, 0x69, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x11, + 0x68, 0x61, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, + 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x68, 0x61, 0x73, 0x49, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x95, 0x01, 0x0a, 0x08, 0x4c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x70, 0x70, 0x69, + 0x6e, 0x67, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x23, + 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x6e, 0x65, 0x52, 0x04, 0x6c, + 0x69, 0x6e, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x64, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x46, 0x6f, 0x6c, 0x64, 0x65, 0x64, + 0x22, 0x53, 0x0a, 0x04, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x63, + 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x22, 0x8a, 0x01, 0x0a, 0x08, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x73, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x6e, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x69, + 0x6e, 0x65, 0x42, 0x5c, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x65, 0x72, 0x66, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x42, 0x0c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x6f, 0x2f, 0x71, 0x72, 0x79, 0x6e, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x66, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x76, 0x31, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +}) + +var ( + file_google_v1_profile_proto_rawDescOnce sync.Once + file_google_v1_profile_proto_rawDescData []byte +) + +func file_google_v1_profile_proto_rawDescGZIP() []byte { + file_google_v1_profile_proto_rawDescOnce.Do(func() { + file_google_v1_profile_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_google_v1_profile_proto_rawDesc), len(file_google_v1_profile_proto_rawDesc))) + }) + return file_google_v1_profile_proto_rawDescData +} + +var file_google_v1_profile_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_google_v1_profile_proto_goTypes = []any{ + (*Profile)(nil), // 0: google.v1.Profile + (*ValueType)(nil), // 1: google.v1.ValueType + (*Sample)(nil), // 2: google.v1.Sample + (*Label)(nil), // 3: google.v1.Label + (*Mapping)(nil), // 4: google.v1.Mapping + (*Location)(nil), // 5: google.v1.Location + (*Line)(nil), // 6: google.v1.Line + (*Function)(nil), // 7: google.v1.Function +} +var file_google_v1_profile_proto_depIdxs = []int32{ + 1, // 0: google.v1.Profile.sample_type:type_name -> google.v1.ValueType + 2, // 1: google.v1.Profile.sample:type_name -> google.v1.Sample + 4, // 2: google.v1.Profile.mapping:type_name -> google.v1.Mapping + 5, // 3: google.v1.Profile.location:type_name -> google.v1.Location + 7, // 4: google.v1.Profile.function:type_name -> google.v1.Function + 1, // 5: google.v1.Profile.period_type:type_name -> google.v1.ValueType + 3, // 6: google.v1.Sample.label:type_name -> google.v1.Label + 6, // 7: google.v1.Location.line:type_name -> google.v1.Line + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name +} + +func init() { file_google_v1_profile_proto_init() } +func file_google_v1_profile_proto_init() { + if File_google_v1_profile_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_google_v1_profile_proto_rawDesc), len(file_google_v1_profile_proto_rawDesc)), + NumEnums: 0, + NumMessages: 8, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_google_v1_profile_proto_goTypes, + DependencyIndexes: file_google_v1_profile_proto_depIdxs, + MessageInfos: file_google_v1_profile_proto_msgTypes, + }.Build() + File_google_v1_profile_proto = out.File + file_google_v1_profile_proto_goTypes = nil + file_google_v1_profile_proto_depIdxs = nil +} diff --git a/reader/prof/parser/lexer.go b/reader/prof/parser/lexer.go new file mode 100644 index 00000000..2177c82b --- /dev/null +++ b/reader/prof/parser/lexer.go @@ -0,0 +1,31 @@ +package parser + +import ( + "github.com/alecthomas/participle/v2" + "github.com/alecthomas/participle/v2/lexer" +) + +var LogQLLexerRulesV2 = []lexer.SimpleRule{ + {"Ocb", `\{`}, + {"Ccb", `\}`}, + {"Comma", `,`}, + + {"Neq", `!=`}, + {"Re", `=~`}, + {"Nre", `!~`}, + {"Eq", `=`}, + + {"Dot", `\.`}, + + {"Label_name", `[a-zA-Z_][a-zA-Z0-9_]*`}, + {"Quoted_string", `"([^"\\]|\\.)*"`}, + {"Ticked_string", "`[^`]*`"}, + + {"Integer", "[0-9]+"}, + + {"space", `\s+`}, +} + +var ProfLexerDefinition = lexer.MustSimple(LogQLLexerRulesV2) +var Parser = participle.MustBuild[Script]( + participle.Lexer(ProfLexerDefinition)) diff --git a/reader/prof/parser/model.go b/reader/prof/parser/model.go new file mode 100644 index 00000000..6d03a609 --- /dev/null +++ b/reader/prof/parser/model.go @@ -0,0 +1,27 @@ +package parser + +import ( + "strconv" + "strings" +) + +type Script struct { + Selectors []Selector `"{" @@? ("," @@ )* ","? "}" ` +} + +type Selector struct { + Name string `@Label_name` + Op string `@("="|"!="|"=~"|"!~")` + Val Str `@@` +} + +type Str struct { + Str string `@(Quoted_string|Ticked_string)` +} + +func (s Str) Unquote() (string, error) { + if s.Str[0] == '`' { + return strings.Trim(s.Str, "`"), nil + } + return strconv.Unquote(s.Str) +} diff --git a/reader/prof/parser/parser.go b/reader/prof/parser/parser.go new file mode 100644 index 00000000..83b50718 --- /dev/null +++ b/reader/prof/parser/parser.go @@ -0,0 +1,11 @@ +package parser + +import "regexp" + +var parseReg = regexp.MustCompile( + "^\\{\\s*([a-zA-Z_][0-9a-zA-Z_]*)\\s*(=~|!~|=|!=)\\s*(`[^`]+`|\"([^\"]|\\.)*\")(\\s*,\\s*([a-zA-Z_][0-9a-zA-Z_]*)\\s*(=~|!~|=|!=)\\s*(`[^`]+`|\"([^\"]|\\.)*\"))*}$", +) + +func Parse(query string) (*Script, error) { + return Parser.ParseString("", query) +} diff --git a/reader/prof/parser/parser_test.go b/reader/prof/parser/parser_test.go new file mode 100644 index 00000000..0bfe2c25 --- /dev/null +++ b/reader/prof/parser/parser_test.go @@ -0,0 +1 @@ +package parser diff --git a/reader/prof/planner.go b/reader/prof/planner.go new file mode 100644 index 00000000..2fe471be --- /dev/null +++ b/reader/prof/planner.go @@ -0,0 +1,88 @@ +package prof + +import ( + "context" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/prof/parser" + shared2 "github.com/metrico/qryn/reader/prof/shared" + "github.com/metrico/qryn/reader/prof/transpiler" + v1 "github.com/metrico/qryn/reader/prof/types/v1" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "github.com/metrico/qryn/reader/utils/tables" + "time" +) + +func PlanLabelNames(ctx context.Context, scripts []*parser.Script, from time.Time, to time.Time, + db *model.DataDatabasesMap) (sql.ISelect, error) { + planner, err := transpiler.PlanLabelNames(scripts) + if err != nil { + return nil, err + } + return planner.Process(plannerCtx(ctx, db, from, to)) +} + +func PlanLabelValues(ctx context.Context, scripts []*parser.Script, labelName string, from time.Time, to time.Time, + db *model.DataDatabasesMap) (sql.ISelect, error) { + planner, err := transpiler.PlanLabelValues(scripts, labelName) + if err != nil { + return nil, err + } + return planner.Process(plannerCtx(ctx, db, from, to)) +} + +func PlanMergeTraces(ctx context.Context, script *parser.Script, typeId *shared2.TypeId, + from time.Time, to time.Time, db *model.DataDatabasesMap) (sql.ISelect, error) { + planner, err := transpiler.PlanMergeTraces(script, typeId) + if err != nil { + return nil, err + } + return planner.Process(plannerCtx(ctx, db, from, to)) +} + +func PlanSelectSeries(ctx context.Context, script *parser.Script, tId *shared2.TypeId, groupBy []string, + agg v1.TimeSeriesAggregationType, step int64, from time.Time, to time.Time, + db *model.DataDatabasesMap) (sql.ISelect, error) { + planner, err := transpiler.PlanSelectSeries(script, tId, groupBy, agg, step) + if err != nil { + return nil, err + } + return planner.Process(plannerCtx(ctx, db, from, to)) +} + +func PlanMergeProfiles(ctx context.Context, script *parser.Script, typeId *shared2.TypeId, + from time.Time, to time.Time, db *model.DataDatabasesMap) (sql.ISelect, error) { + planner, err := transpiler.PlanMergeProfiles(script, typeId) + if err != nil { + return nil, err + } + return planner.Process(plannerCtx(ctx, db, from, to)) +} + +func PlanSeries(ctx context.Context, scripts []*parser.Script, + labelNames []string, from time.Time, to time.Time, db *model.DataDatabasesMap) (sql.ISelect, error) { + planner, err := transpiler.PlanSeries(scripts, labelNames) + if err != nil { + return nil, err + } + return planner.Process(plannerCtx(ctx, db, from, to)) +} + +func PlanAnalyzeQuery(ctx context.Context, script *parser.Script, + from time.Time, to time.Time, db *model.DataDatabasesMap) (sql.ISelect, error) { + planner, err := transpiler.PlanAnalyzeQuery(script) + if err != nil { + return nil, err + } + return planner.Process(plannerCtx(ctx, db, from, to)) +} + +func plannerCtx(ctx context.Context, db *model.DataDatabasesMap, from, to time.Time) *shared.PlannerContext { + sqlCtx := shared.PlannerContext{ + From: from, + To: to, + Ctx: ctx, + } + tables.PopulateTableNames(&sqlCtx, db) + return &sqlCtx +} diff --git a/reader/prof/profile.pb.go b/reader/prof/profile.pb.go new file mode 100644 index 00000000..3d68d5b3 --- /dev/null +++ b/reader/prof/profile.pb.go @@ -0,0 +1,1005 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Profile is a common stacktrace profile format. +// +// Measurements represented with this format should follow the +// following conventions: +// +// - Consumers should treat unset optional fields as if they had been +// set with their default value. +// +// - When possible, measurements should be stored in "unsampled" form +// that is most useful to humans. There should be enough +// information present to determine the original sampled values. +// +// - On-disk, the serialized proto must be gzip-compressed. +// +// - The profile is represented as a set of samples, where each sample +// references a sequence of locations, and where each location belongs +// to a mapping. +// - There is a N->1 relationship from sample.location_id entries to +// locations. For every sample.location_id entry there must be a +// unique Location with that id. +// - There is an optional N->1 relationship from locations to +// mappings. For every nonzero Location.mapping_id there must be a +// unique Mapping with that id. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.4 +// protoc v3.21.12 +// source: profile.proto + +package prof + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Profile struct { + state protoimpl.MessageState `protogen:"open.v1"` + // A description of the samples associated with each Sample.value. + // For a cpu profile this might be: + // + // [["cpu","nanoseconds"]] or [["wall","seconds"]] or [["syscall","count"]] + // + // For a heap profile, this might be: + // + // [["allocations","count"], ["space","bytes"]], + // + // If one of the values represents the number of events represented + // by the sample, by convention it should be at index 0 and use + // sample_type.unit == "count". + SampleType []*ValueType `protobuf:"bytes,1,rep,name=sample_type,json=sampleType,proto3" json:"sample_type,omitempty"` + // The set of samples recorded in this profile. + Sample []*Sample `protobuf:"bytes,2,rep,name=sample,proto3" json:"sample,omitempty"` + // Mapping from address ranges to the image/binary/library mapped + // into that address range. mapping[0] will be the main binary. + Mapping []*Mapping `protobuf:"bytes,3,rep,name=mapping,proto3" json:"mapping,omitempty"` + // Locations referenced by samples. + Location []*Location `protobuf:"bytes,4,rep,name=location,proto3" json:"location,omitempty"` + // Functions referenced by locations. + Function []*Function `protobuf:"bytes,5,rep,name=function,proto3" json:"function,omitempty"` + // A common table for strings referenced by various messages. + // string_table[0] must always be "". + StringTable []string `protobuf:"bytes,6,rep,name=string_table,json=stringTable,proto3" json:"string_table,omitempty"` + // frames with Function.function_name fully matching the following + // regexp will be dropped from the samples, along with their successors. + DropFrames int64 `protobuf:"varint,7,opt,name=drop_frames,json=dropFrames,proto3" json:"drop_frames,omitempty"` // Index into string table. + // frames with Function.function_name fully matching the following + // regexp will be kept, even if it matches drop_frames. + KeepFrames int64 `protobuf:"varint,8,opt,name=keep_frames,json=keepFrames,proto3" json:"keep_frames,omitempty"` // Index into string table. + // Time of collection (UTC) represented as nanoseconds past the epoch. + TimeNanos int64 `protobuf:"varint,9,opt,name=time_nanos,json=timeNanos,proto3" json:"time_nanos,omitempty"` + // Duration of the profile, if a duration makes sense. + DurationNanos int64 `protobuf:"varint,10,opt,name=duration_nanos,json=durationNanos,proto3" json:"duration_nanos,omitempty"` + // The kind of events between sampled occurrences. + // e.g [ "cpu","cycles" ] or [ "heap","bytes" ] + PeriodType *ValueType `protobuf:"bytes,11,opt,name=period_type,json=periodType,proto3" json:"period_type,omitempty"` + // The number of events between sampled occurrences. + Period int64 `protobuf:"varint,12,opt,name=period,proto3" json:"period,omitempty"` + // Free-form text associated with the profile. The text is displayed as is + // to the user by the tools that read profiles (e.g. by pprof). This field + // should not be used to store any machine-readable information, it is only + // for human-friendly content. The profile must stay functional if this field + // is cleaned. + Comment []int64 `protobuf:"varint,13,rep,packed,name=comment,proto3" json:"comment,omitempty"` // Indices into string table. + // Index into the string table of the type of the preferred sample + // value. If unset, clients should default to the last sample value. + DefaultSampleType int64 `protobuf:"varint,14,opt,name=default_sample_type,json=defaultSampleType,proto3" json:"default_sample_type,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Profile) Reset() { + *x = Profile{} + mi := &file_profile_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Profile) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Profile) ProtoMessage() {} + +func (x *Profile) ProtoReflect() protoreflect.Message { + mi := &file_profile_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Profile.ProtoReflect.Descriptor instead. +func (*Profile) Descriptor() ([]byte, []int) { + return file_profile_proto_rawDescGZIP(), []int{0} +} + +func (x *Profile) GetSampleType() []*ValueType { + if x != nil { + return x.SampleType + } + return nil +} + +func (x *Profile) GetSample() []*Sample { + if x != nil { + return x.Sample + } + return nil +} + +func (x *Profile) GetMapping() []*Mapping { + if x != nil { + return x.Mapping + } + return nil +} + +func (x *Profile) GetLocation() []*Location { + if x != nil { + return x.Location + } + return nil +} + +func (x *Profile) GetFunction() []*Function { + if x != nil { + return x.Function + } + return nil +} + +func (x *Profile) GetStringTable() []string { + if x != nil { + return x.StringTable + } + return nil +} + +func (x *Profile) GetDropFrames() int64 { + if x != nil { + return x.DropFrames + } + return 0 +} + +func (x *Profile) GetKeepFrames() int64 { + if x != nil { + return x.KeepFrames + } + return 0 +} + +func (x *Profile) GetTimeNanos() int64 { + if x != nil { + return x.TimeNanos + } + return 0 +} + +func (x *Profile) GetDurationNanos() int64 { + if x != nil { + return x.DurationNanos + } + return 0 +} + +func (x *Profile) GetPeriodType() *ValueType { + if x != nil { + return x.PeriodType + } + return nil +} + +func (x *Profile) GetPeriod() int64 { + if x != nil { + return x.Period + } + return 0 +} + +func (x *Profile) GetComment() []int64 { + if x != nil { + return x.Comment + } + return nil +} + +func (x *Profile) GetDefaultSampleType() int64 { + if x != nil { + return x.DefaultSampleType + } + return 0 +} + +// ValueType describes the semantics and measurement units of a value. +type ValueType struct { + state protoimpl.MessageState `protogen:"open.v1"` + Type int64 `protobuf:"varint,1,opt,name=type,proto3" json:"type,omitempty"` // Index into string table. + Unit int64 `protobuf:"varint,2,opt,name=unit,proto3" json:"unit,omitempty"` // Index into string table. + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ValueType) Reset() { + *x = ValueType{} + mi := &file_profile_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ValueType) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValueType) ProtoMessage() {} + +func (x *ValueType) ProtoReflect() protoreflect.Message { + mi := &file_profile_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValueType.ProtoReflect.Descriptor instead. +func (*ValueType) Descriptor() ([]byte, []int) { + return file_profile_proto_rawDescGZIP(), []int{1} +} + +func (x *ValueType) GetType() int64 { + if x != nil { + return x.Type + } + return 0 +} + +func (x *ValueType) GetUnit() int64 { + if x != nil { + return x.Unit + } + return 0 +} + +// Each Sample records values encountered in some program +// context. The program context is typically a stack trace, perhaps +// augmented with auxiliary information like the thread-id, some +// indicator of a higher level request being handled etc. +type Sample struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The ids recorded here correspond to a Profile.location.id. + // The leaf is at location_id[0]. + LocationId []uint64 `protobuf:"varint,1,rep,packed,name=location_id,json=locationId,proto3" json:"location_id,omitempty"` + // The type and unit of each value is defined by the corresponding + // entry in Profile.sample_type. All samples must have the same + // number of values, the same as the length of Profile.sample_type. + // When aggregating multiple samples into a single sample, the + // result has a list of values that is the element-wise sum of the + // lists of the originals. + Value []int64 `protobuf:"varint,2,rep,packed,name=value,proto3" json:"value,omitempty"` + // label includes additional context for this sample. It can include + // things like a thread id, allocation size, etc. + // + // NOTE: While possible, having multiple values for the same label key is + // strongly discouraged and should never be used. Most tools (e.g. pprof) do + // not have good (or any) support for multi-value labels. And an even more + // discouraged case is having a string label and a numeric label of the same + // name on a sample. Again, possible to express, but should not be used. + Label []*Label `protobuf:"bytes,3,rep,name=label,proto3" json:"label,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Sample) Reset() { + *x = Sample{} + mi := &file_profile_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Sample) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Sample) ProtoMessage() {} + +func (x *Sample) ProtoReflect() protoreflect.Message { + mi := &file_profile_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Sample.ProtoReflect.Descriptor instead. +func (*Sample) Descriptor() ([]byte, []int) { + return file_profile_proto_rawDescGZIP(), []int{2} +} + +func (x *Sample) GetLocationId() []uint64 { + if x != nil { + return x.LocationId + } + return nil +} + +func (x *Sample) GetValue() []int64 { + if x != nil { + return x.Value + } + return nil +} + +func (x *Sample) GetLabel() []*Label { + if x != nil { + return x.Label + } + return nil +} + +type Label struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Index into string table. An annotation for a sample (e.g. + // "allocation_size") with an associated value. + // Keys with "pprof::" prefix are reserved for internal use by pprof. + Key int64 `protobuf:"varint,1,opt,name=key,proto3" json:"key,omitempty"` + // At most one of the following must be present + Str int64 `protobuf:"varint,2,opt,name=str,proto3" json:"str,omitempty"` // Index into string table + Num int64 `protobuf:"varint,3,opt,name=num,proto3" json:"num,omitempty"` + // Should only be present when num is present. + // Specifies the units of num. + // Use arbitrary string (for example, "requests") as a custom count unit. + // If no unit is specified, consumer may apply heuristic to deduce the unit. + // Consumers may also interpret units like "bytes" and "kilobytes" as memory + // units and units like "seconds" and "nanoseconds" as time units, + // and apply appropriate unit conversions to these. + NumUnit int64 `protobuf:"varint,4,opt,name=num_unit,json=numUnit,proto3" json:"num_unit,omitempty"` // Index into string table + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Label) Reset() { + *x = Label{} + mi := &file_profile_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Label) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Label) ProtoMessage() {} + +func (x *Label) ProtoReflect() protoreflect.Message { + mi := &file_profile_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Label.ProtoReflect.Descriptor instead. +func (*Label) Descriptor() ([]byte, []int) { + return file_profile_proto_rawDescGZIP(), []int{3} +} + +func (x *Label) GetKey() int64 { + if x != nil { + return x.Key + } + return 0 +} + +func (x *Label) GetStr() int64 { + if x != nil { + return x.Str + } + return 0 +} + +func (x *Label) GetNum() int64 { + if x != nil { + return x.Num + } + return 0 +} + +func (x *Label) GetNumUnit() int64 { + if x != nil { + return x.NumUnit + } + return 0 +} + +type Mapping struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Unique nonzero id for the mapping. + Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + // Address at which the binary (or DLL) is loaded into memory. + MemoryStart uint64 `protobuf:"varint,2,opt,name=memory_start,json=memoryStart,proto3" json:"memory_start,omitempty"` + // The limit of the address range occupied by this mapping. + MemoryLimit uint64 `protobuf:"varint,3,opt,name=memory_limit,json=memoryLimit,proto3" json:"memory_limit,omitempty"` + // Offset in the binary that corresponds to the first mapped address. + FileOffset uint64 `protobuf:"varint,4,opt,name=file_offset,json=fileOffset,proto3" json:"file_offset,omitempty"` + // The object this entry is loaded from. This can be a filename on + // disk for the main binary and shared libraries, or virtual + // abstractions like "[vdso]". + Filename int64 `protobuf:"varint,5,opt,name=filename,proto3" json:"filename,omitempty"` // Index into string table + // A string that uniquely identifies a particular program version + // with high probability. E.g., for binaries generated by GNU tools, + // it could be the contents of the .note.gnu.build-id field. + BuildId int64 `protobuf:"varint,6,opt,name=build_id,json=buildId,proto3" json:"build_id,omitempty"` // Index into string table + // The following fields indicate the resolution of symbolic info. + HasFunctions bool `protobuf:"varint,7,opt,name=has_functions,json=hasFunctions,proto3" json:"has_functions,omitempty"` + HasFilenames bool `protobuf:"varint,8,opt,name=has_filenames,json=hasFilenames,proto3" json:"has_filenames,omitempty"` + HasLineNumbers bool `protobuf:"varint,9,opt,name=has_line_numbers,json=hasLineNumbers,proto3" json:"has_line_numbers,omitempty"` + HasInlineFrames bool `protobuf:"varint,10,opt,name=has_inline_frames,json=hasInlineFrames,proto3" json:"has_inline_frames,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Mapping) Reset() { + *x = Mapping{} + mi := &file_profile_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Mapping) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Mapping) ProtoMessage() {} + +func (x *Mapping) ProtoReflect() protoreflect.Message { + mi := &file_profile_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Mapping.ProtoReflect.Descriptor instead. +func (*Mapping) Descriptor() ([]byte, []int) { + return file_profile_proto_rawDescGZIP(), []int{4} +} + +func (x *Mapping) GetId() uint64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Mapping) GetMemoryStart() uint64 { + if x != nil { + return x.MemoryStart + } + return 0 +} + +func (x *Mapping) GetMemoryLimit() uint64 { + if x != nil { + return x.MemoryLimit + } + return 0 +} + +func (x *Mapping) GetFileOffset() uint64 { + if x != nil { + return x.FileOffset + } + return 0 +} + +func (x *Mapping) GetFilename() int64 { + if x != nil { + return x.Filename + } + return 0 +} + +func (x *Mapping) GetBuildId() int64 { + if x != nil { + return x.BuildId + } + return 0 +} + +func (x *Mapping) GetHasFunctions() bool { + if x != nil { + return x.HasFunctions + } + return false +} + +func (x *Mapping) GetHasFilenames() bool { + if x != nil { + return x.HasFilenames + } + return false +} + +func (x *Mapping) GetHasLineNumbers() bool { + if x != nil { + return x.HasLineNumbers + } + return false +} + +func (x *Mapping) GetHasInlineFrames() bool { + if x != nil { + return x.HasInlineFrames + } + return false +} + +// Describes function and line table debug information. +type Location struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Unique nonzero id for the location. A profile could use + // instruction addresses or any integer sequence as ids. + Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + // The id of the corresponding profile.Mapping for this location. + // It can be unset if the mapping is unknown or not applicable for + // this profile type. + MappingId uint64 `protobuf:"varint,2,opt,name=mapping_id,json=mappingId,proto3" json:"mapping_id,omitempty"` + // The instruction address for this location, if available. It + // should be within [Mapping.memory_start...Mapping.memory_limit] + // for the corresponding mapping. A non-leaf address may be in the + // middle of a call instruction. It is up to display tools to find + // the beginning of the instruction if necessary. + Address uint64 `protobuf:"varint,3,opt,name=address,proto3" json:"address,omitempty"` + // Multiple line indicates this location has inlined functions, + // where the last entry represents the caller into which the + // preceding entries were inlined. + // + // E.g., if memcpy() is inlined into printf: + // + // line[0].function_name == "memcpy" + // line[1].function_name == "printf" + Line []*Line `protobuf:"bytes,4,rep,name=line,proto3" json:"line,omitempty"` + // Provides an indication that multiple symbols map to this location's + // address, for example due to identical code folding by the linker. In that + // case the line information above represents one of the multiple + // symbols. This field must be recomputed when the symbolization state of the + // profile changes. + IsFolded bool `protobuf:"varint,5,opt,name=is_folded,json=isFolded,proto3" json:"is_folded,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Location) Reset() { + *x = Location{} + mi := &file_profile_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Location) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Location) ProtoMessage() {} + +func (x *Location) ProtoReflect() protoreflect.Message { + mi := &file_profile_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Location.ProtoReflect.Descriptor instead. +func (*Location) Descriptor() ([]byte, []int) { + return file_profile_proto_rawDescGZIP(), []int{5} +} + +func (x *Location) GetId() uint64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Location) GetMappingId() uint64 { + if x != nil { + return x.MappingId + } + return 0 +} + +func (x *Location) GetAddress() uint64 { + if x != nil { + return x.Address + } + return 0 +} + +func (x *Location) GetLine() []*Line { + if x != nil { + return x.Line + } + return nil +} + +func (x *Location) GetIsFolded() bool { + if x != nil { + return x.IsFolded + } + return false +} + +type Line struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The id of the corresponding profile.Function for this line. + FunctionId uint64 `protobuf:"varint,1,opt,name=function_id,json=functionId,proto3" json:"function_id,omitempty"` + // Line number in source code. + Line int64 `protobuf:"varint,2,opt,name=line,proto3" json:"line,omitempty"` + // Column number in source code. + Column int64 `protobuf:"varint,3,opt,name=column,proto3" json:"column,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Line) Reset() { + *x = Line{} + mi := &file_profile_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Line) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Line) ProtoMessage() {} + +func (x *Line) ProtoReflect() protoreflect.Message { + mi := &file_profile_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Line.ProtoReflect.Descriptor instead. +func (*Line) Descriptor() ([]byte, []int) { + return file_profile_proto_rawDescGZIP(), []int{6} +} + +func (x *Line) GetFunctionId() uint64 { + if x != nil { + return x.FunctionId + } + return 0 +} + +func (x *Line) GetLine() int64 { + if x != nil { + return x.Line + } + return 0 +} + +func (x *Line) GetColumn() int64 { + if x != nil { + return x.Column + } + return 0 +} + +type Function struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Unique nonzero id for the function. + Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + // Name of the function, in human-readable form if available. + Name int64 `protobuf:"varint,2,opt,name=name,proto3" json:"name,omitempty"` // Index into string table + // Name of the function, as identified by the system. + // For instance, it can be a C++ mangled name. + SystemName int64 `protobuf:"varint,3,opt,name=system_name,json=systemName,proto3" json:"system_name,omitempty"` // Index into string table + // Source file containing the function. + Filename int64 `protobuf:"varint,4,opt,name=filename,proto3" json:"filename,omitempty"` // Index into string table + // Line number in source file. + StartLine int64 `protobuf:"varint,5,opt,name=start_line,json=startLine,proto3" json:"start_line,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Function) Reset() { + *x = Function{} + mi := &file_profile_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Function) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Function) ProtoMessage() {} + +func (x *Function) ProtoReflect() protoreflect.Message { + mi := &file_profile_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Function.ProtoReflect.Descriptor instead. +func (*Function) Descriptor() ([]byte, []int) { + return file_profile_proto_rawDescGZIP(), []int{7} +} + +func (x *Function) GetId() uint64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Function) GetName() int64 { + if x != nil { + return x.Name + } + return 0 +} + +func (x *Function) GetSystemName() int64 { + if x != nil { + return x.SystemName + } + return 0 +} + +func (x *Function) GetFilename() int64 { + if x != nil { + return x.Filename + } + return 0 +} + +func (x *Function) GetStartLine() int64 { + if x != nil { + return x.StartLine + } + return 0 +} + +var File_profile_proto protoreflect.FileDescriptor + +var file_profile_proto_rawDesc = string([]byte{ + 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x12, 0x70, 0x65, 0x72, 0x66, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x22, 0xf5, 0x04, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, + 0x3e, 0x0a, 0x0b, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x74, 0x6f, 0x6f, 0x6c, 0x73, + 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x52, 0x0a, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x32, 0x0a, 0x06, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x06, 0x73, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x12, 0x35, 0x0a, 0x07, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x74, 0x6f, 0x6f, 0x6c, 0x73, + 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, + 0x67, 0x52, 0x07, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x38, 0x0a, 0x08, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, + 0x65, 0x72, 0x66, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x08, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x74, 0x6f, 0x6f, + 0x6c, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x46, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, + 0x0a, 0x0c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x06, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x72, 0x6f, 0x70, 0x46, 0x72, 0x61, 0x6d, + 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, + 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x46, 0x72, 0x61, + 0x6d, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, + 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x4e, 0x61, 0x6e, + 0x6f, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, + 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x64, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x3e, 0x0a, 0x0b, 0x70, 0x65, 0x72, + 0x69, 0x6f, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, + 0x2e, 0x70, 0x65, 0x72, 0x66, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x70, + 0x65, 0x72, 0x69, 0x6f, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x65, 0x72, + 0x69, 0x6f, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x70, 0x65, 0x72, 0x69, 0x6f, + 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x0d, 0x20, 0x03, + 0x28, 0x03, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, + 0x74, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x22, 0x33, 0x0a, 0x09, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x75, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x75, 0x6e, 0x69, 0x74, + 0x22, 0x70, 0x0a, 0x06, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x52, + 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x2e, 0x70, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x61, 0x62, + 0x65, 0x6c, 0x22, 0x58, 0x0a, 0x05, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x73, 0x74, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x73, 0x74, 0x72, 0x12, + 0x10, 0x0a, 0x03, 0x6e, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x6e, 0x75, + 0x6d, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x75, 0x6d, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x07, 0x6e, 0x75, 0x6d, 0x55, 0x6e, 0x69, 0x74, 0x22, 0xd7, 0x02, 0x0a, + 0x07, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, + 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0b, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1f, + 0x0a, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, + 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x68, 0x61, 0x73, 0x5f, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x68, + 0x61, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x68, + 0x61, 0x73, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0c, 0x68, 0x61, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x12, 0x28, 0x0a, 0x10, 0x68, 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x6e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x68, 0x61, 0x73, 0x4c, + 0x69, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x61, + 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x68, 0x61, 0x73, 0x49, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x9e, 0x01, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, + 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x04, + 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x65, 0x72, + 0x66, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, + 0x4c, 0x69, 0x6e, 0x65, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, + 0x5f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, + 0x73, 0x46, 0x6f, 0x6c, 0x64, 0x65, 0x64, 0x22, 0x53, 0x0a, 0x04, 0x4c, 0x69, 0x6e, 0x65, 0x12, + 0x1f, 0x0a, 0x0b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, + 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x22, 0x8a, 0x01, 0x0a, + 0x08, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, + 0x0b, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x69, 0x6e, 0x65, 0x42, 0x52, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x74, 0x6f, 0x6f, 0x6c, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x42, 0x0c, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x23, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x6f, 0x2f, 0x71, 0x72, 0x79, + 0x6e, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x66, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +}) + +var ( + file_profile_proto_rawDescOnce sync.Once + file_profile_proto_rawDescData []byte +) + +func file_profile_proto_rawDescGZIP() []byte { + file_profile_proto_rawDescOnce.Do(func() { + file_profile_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_profile_proto_rawDesc), len(file_profile_proto_rawDesc))) + }) + return file_profile_proto_rawDescData +} + +var file_profile_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_profile_proto_goTypes = []any{ + (*Profile)(nil), // 0: perftools.profiles.Profile + (*ValueType)(nil), // 1: perftools.profiles.ValueType + (*Sample)(nil), // 2: perftools.profiles.Sample + (*Label)(nil), // 3: perftools.profiles.Label + (*Mapping)(nil), // 4: perftools.profiles.Mapping + (*Location)(nil), // 5: perftools.profiles.Location + (*Line)(nil), // 6: perftools.profiles.Line + (*Function)(nil), // 7: perftools.profiles.Function +} +var file_profile_proto_depIdxs = []int32{ + 1, // 0: perftools.profiles.Profile.sample_type:type_name -> perftools.profiles.ValueType + 2, // 1: perftools.profiles.Profile.sample:type_name -> perftools.profiles.Sample + 4, // 2: perftools.profiles.Profile.mapping:type_name -> perftools.profiles.Mapping + 5, // 3: perftools.profiles.Profile.location:type_name -> perftools.profiles.Location + 7, // 4: perftools.profiles.Profile.function:type_name -> perftools.profiles.Function + 1, // 5: perftools.profiles.Profile.period_type:type_name -> perftools.profiles.ValueType + 3, // 6: perftools.profiles.Sample.label:type_name -> perftools.profiles.Label + 6, // 7: perftools.profiles.Location.line:type_name -> perftools.profiles.Line + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name +} + +func init() { file_profile_proto_init() } +func file_profile_proto_init() { + if File_profile_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_profile_proto_rawDesc), len(file_profile_proto_rawDesc)), + NumEnums: 0, + NumMessages: 8, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_profile_proto_goTypes, + DependencyIndexes: file_profile_proto_depIdxs, + MessageInfos: file_profile_proto_msgTypes, + }.Build() + File_profile_proto = out.File + file_profile_proto_goTypes = nil + file_profile_proto_depIdxs = nil +} diff --git a/pyroscope/proto/google/v1/profile.proto b/reader/prof/proto/google/v1/profile.proto similarity index 99% rename from pyroscope/proto/google/v1/profile.proto rename to reader/prof/proto/google/v1/profile.proto index ca40b369..1629d834 100644 --- a/pyroscope/proto/google/v1/profile.proto +++ b/reader/prof/proto/google/v1/profile.proto @@ -42,6 +42,7 @@ package google.v1; option java_outer_classname = "ProfileProto"; option java_package = "com.google.perftools.profiles"; +option go_package = "github.com/metrico/qryn/reader/prof/google/v1"; message Profile { // A description of the samples associated with each Sample.value. diff --git a/pyroscope/proto/profile.proto b/reader/prof/proto/profile.proto similarity index 99% rename from pyroscope/proto/profile.proto rename to reader/prof/proto/profile.proto index ff987a61..427b1dc6 100644 --- a/pyroscope/proto/profile.proto +++ b/reader/prof/proto/profile.proto @@ -42,6 +42,7 @@ package perftools.profiles; option java_package = "com.google.perftools.profiles"; option java_outer_classname = "ProfileProto"; +option go_package="github.com/metrico/qryn/reader/prof"; message Profile { // A description of the samples associated with each Sample.value. diff --git a/pyroscope/proto/querier.proto b/reader/prof/proto/querier.proto similarity index 99% rename from pyroscope/proto/querier.proto rename to reader/prof/proto/querier.proto index 78ae0da0..d7e9dc90 100644 --- a/pyroscope/proto/querier.proto +++ b/reader/prof/proto/querier.proto @@ -1,6 +1,7 @@ syntax = "proto3"; package querier.v1; +option go_package="github.com/metrico/qryn/reader/prof"; import "google/v1/profile.proto"; import "types/v1/types.proto"; diff --git a/pyroscope/proto/settings.proto b/reader/prof/proto/settings.proto similarity index 89% rename from pyroscope/proto/settings.proto rename to reader/prof/proto/settings.proto index fb7375bf..68c4ea07 100644 --- a/pyroscope/proto/settings.proto +++ b/reader/prof/proto/settings.proto @@ -1,6 +1,7 @@ syntax = "proto3"; package settings.v1; +option go_package="github.com/metrico/qryn/reader/prof"; service SettingsService { rpc Get(GetSettingsRequest) returns (GetSettingsResponse) {} diff --git a/pyroscope/proto/types/v1/types.proto b/reader/prof/proto/types/v1/types.proto similarity index 97% rename from pyroscope/proto/types/v1/types.proto rename to reader/prof/proto/types/v1/types.proto index ced94bf2..f9730d14 100644 --- a/pyroscope/proto/types/v1/types.proto +++ b/reader/prof/proto/types/v1/types.proto @@ -1,6 +1,7 @@ syntax = "proto3"; package types.v1; +option go_package = "github.com/metrico/qryn/reader/prof/types/v1"; message LabelPair { string name = 1; diff --git a/reader/prof/querier.pb.go b/reader/prof/querier.pb.go new file mode 100644 index 00000000..940459ed --- /dev/null +++ b/reader/prof/querier.pb.go @@ -0,0 +1,1792 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.4 +// protoc v3.21.12 +// source: querier.proto + +package prof + +import ( + v11 "github.com/metrico/qryn/reader/prof/google/v1" + v1 "github.com/metrico/qryn/reader/prof/types/v1" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ProfileFormat int32 + +const ( + ProfileFormat_PROFILE_FORMAT_UNSPECIFIED ProfileFormat = 0 + ProfileFormat_PROFILE_FORMAT_FLAMEGRAPH ProfileFormat = 1 + ProfileFormat_PROFILE_FORMAT_TREE ProfileFormat = 2 +) + +// Enum value maps for ProfileFormat. +var ( + ProfileFormat_name = map[int32]string{ + 0: "PROFILE_FORMAT_UNSPECIFIED", + 1: "PROFILE_FORMAT_FLAMEGRAPH", + 2: "PROFILE_FORMAT_TREE", + } + ProfileFormat_value = map[string]int32{ + "PROFILE_FORMAT_UNSPECIFIED": 0, + "PROFILE_FORMAT_FLAMEGRAPH": 1, + "PROFILE_FORMAT_TREE": 2, + } +) + +func (x ProfileFormat) Enum() *ProfileFormat { + p := new(ProfileFormat) + *p = x + return p +} + +func (x ProfileFormat) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ProfileFormat) Descriptor() protoreflect.EnumDescriptor { + return file_querier_proto_enumTypes[0].Descriptor() +} + +func (ProfileFormat) Type() protoreflect.EnumType { + return &file_querier_proto_enumTypes[0] +} + +func (x ProfileFormat) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ProfileFormat.Descriptor instead. +func (ProfileFormat) EnumDescriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{0} +} + +type ProfileTypesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Milliseconds since epoch. If missing or zero, only the ingesters will be + // queried. + Start int64 `protobuf:"varint,1,opt,name=start,proto3" json:"start,omitempty"` + // Milliseconds since epoch. If missing or zero, only the ingesters will be + // queried. + End int64 `protobuf:"varint,2,opt,name=end,proto3" json:"end,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProfileTypesRequest) Reset() { + *x = ProfileTypesRequest{} + mi := &file_querier_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProfileTypesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProfileTypesRequest) ProtoMessage() {} + +func (x *ProfileTypesRequest) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProfileTypesRequest.ProtoReflect.Descriptor instead. +func (*ProfileTypesRequest) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{0} +} + +func (x *ProfileTypesRequest) GetStart() int64 { + if x != nil { + return x.Start + } + return 0 +} + +func (x *ProfileTypesRequest) GetEnd() int64 { + if x != nil { + return x.End + } + return 0 +} + +type ProfileTypesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + ProfileTypes []*v1.ProfileType `protobuf:"bytes,1,rep,name=profile_types,json=profileTypes,proto3" json:"profile_types,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProfileTypesResponse) Reset() { + *x = ProfileTypesResponse{} + mi := &file_querier_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProfileTypesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProfileTypesResponse) ProtoMessage() {} + +func (x *ProfileTypesResponse) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProfileTypesResponse.ProtoReflect.Descriptor instead. +func (*ProfileTypesResponse) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{1} +} + +func (x *ProfileTypesResponse) GetProfileTypes() []*v1.ProfileType { + if x != nil { + return x.ProfileTypes + } + return nil +} + +type SeriesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Matchers []string `protobuf:"bytes,1,rep,name=matchers,proto3" json:"matchers,omitempty"` + LabelNames []string `protobuf:"bytes,2,rep,name=label_names,json=labelNames,proto3" json:"label_names,omitempty"` + // Milliseconds since epoch. If missing or zero, only the ingesters will be + // queried. + Start int64 `protobuf:"varint,3,opt,name=start,proto3" json:"start,omitempty"` + // Milliseconds since epoch. If missing or zero, only the ingesters will be + // queried. + End int64 `protobuf:"varint,4,opt,name=end,proto3" json:"end,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SeriesRequest) Reset() { + *x = SeriesRequest{} + mi := &file_querier_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SeriesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SeriesRequest) ProtoMessage() {} + +func (x *SeriesRequest) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SeriesRequest.ProtoReflect.Descriptor instead. +func (*SeriesRequest) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{2} +} + +func (x *SeriesRequest) GetMatchers() []string { + if x != nil { + return x.Matchers + } + return nil +} + +func (x *SeriesRequest) GetLabelNames() []string { + if x != nil { + return x.LabelNames + } + return nil +} + +func (x *SeriesRequest) GetStart() int64 { + if x != nil { + return x.Start + } + return 0 +} + +func (x *SeriesRequest) GetEnd() int64 { + if x != nil { + return x.End + } + return 0 +} + +type SeriesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + LabelsSet []*v1.Labels `protobuf:"bytes,2,rep,name=labels_set,json=labelsSet,proto3" json:"labels_set,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SeriesResponse) Reset() { + *x = SeriesResponse{} + mi := &file_querier_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SeriesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SeriesResponse) ProtoMessage() {} + +func (x *SeriesResponse) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SeriesResponse.ProtoReflect.Descriptor instead. +func (*SeriesResponse) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{3} +} + +func (x *SeriesResponse) GetLabelsSet() []*v1.Labels { + if x != nil { + return x.LabelsSet + } + return nil +} + +type SelectMergeStacktracesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ProfileTypeID string `protobuf:"bytes,1,opt,name=profile_typeID,json=profileTypeID,proto3" json:"profile_typeID,omitempty"` + LabelSelector string `protobuf:"bytes,2,opt,name=label_selector,json=labelSelector,proto3" json:"label_selector,omitempty"` + // Milliseconds since epoch. + Start int64 `protobuf:"varint,3,opt,name=start,proto3" json:"start,omitempty"` + // Milliseconds since epoch. + End int64 `protobuf:"varint,4,opt,name=end,proto3" json:"end,omitempty"` + // Limit the nodes returned to only show the node with the max_node's biggest total + MaxNodes *int64 `protobuf:"varint,5,opt,name=max_nodes,json=maxNodes,proto3,oneof" json:"max_nodes,omitempty"` + // Profile format specifies the format of profile to be returned. + // If not specified, the profile will be returned in flame graph format. + Format ProfileFormat `protobuf:"varint,6,opt,name=format,proto3,enum=querier.v1.ProfileFormat" json:"format,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SelectMergeStacktracesRequest) Reset() { + *x = SelectMergeStacktracesRequest{} + mi := &file_querier_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SelectMergeStacktracesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SelectMergeStacktracesRequest) ProtoMessage() {} + +func (x *SelectMergeStacktracesRequest) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SelectMergeStacktracesRequest.ProtoReflect.Descriptor instead. +func (*SelectMergeStacktracesRequest) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{4} +} + +func (x *SelectMergeStacktracesRequest) GetProfileTypeID() string { + if x != nil { + return x.ProfileTypeID + } + return "" +} + +func (x *SelectMergeStacktracesRequest) GetLabelSelector() string { + if x != nil { + return x.LabelSelector + } + return "" +} + +func (x *SelectMergeStacktracesRequest) GetStart() int64 { + if x != nil { + return x.Start + } + return 0 +} + +func (x *SelectMergeStacktracesRequest) GetEnd() int64 { + if x != nil { + return x.End + } + return 0 +} + +func (x *SelectMergeStacktracesRequest) GetMaxNodes() int64 { + if x != nil && x.MaxNodes != nil { + return *x.MaxNodes + } + return 0 +} + +func (x *SelectMergeStacktracesRequest) GetFormat() ProfileFormat { + if x != nil { + return x.Format + } + return ProfileFormat_PROFILE_FORMAT_UNSPECIFIED +} + +type SelectMergeStacktracesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Flamegraph *FlameGraph `protobuf:"bytes,1,opt,name=flamegraph,proto3" json:"flamegraph,omitempty"` + // Pyroscope tree bytes. + Tree []byte `protobuf:"bytes,2,opt,name=tree,proto3" json:"tree,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SelectMergeStacktracesResponse) Reset() { + *x = SelectMergeStacktracesResponse{} + mi := &file_querier_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SelectMergeStacktracesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SelectMergeStacktracesResponse) ProtoMessage() {} + +func (x *SelectMergeStacktracesResponse) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SelectMergeStacktracesResponse.ProtoReflect.Descriptor instead. +func (*SelectMergeStacktracesResponse) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{5} +} + +func (x *SelectMergeStacktracesResponse) GetFlamegraph() *FlameGraph { + if x != nil { + return x.Flamegraph + } + return nil +} + +func (x *SelectMergeStacktracesResponse) GetTree() []byte { + if x != nil { + return x.Tree + } + return nil +} + +type SelectMergeSpanProfileRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ProfileTypeID string `protobuf:"bytes,1,opt,name=profile_typeID,json=profileTypeID,proto3" json:"profile_typeID,omitempty"` + LabelSelector string `protobuf:"bytes,2,opt,name=label_selector,json=labelSelector,proto3" json:"label_selector,omitempty"` + SpanSelector []string `protobuf:"bytes,3,rep,name=span_selector,json=spanSelector,proto3" json:"span_selector,omitempty"` + // Milliseconds since epoch. + Start int64 `protobuf:"varint,4,opt,name=start,proto3" json:"start,omitempty"` + // Milliseconds since epoch. + End int64 `protobuf:"varint,5,opt,name=end,proto3" json:"end,omitempty"` + // Limit the nodes returned to only show the node with the max_node's biggest total + MaxNodes *int64 `protobuf:"varint,6,opt,name=max_nodes,json=maxNodes,proto3,oneof" json:"max_nodes,omitempty"` + // Profile format specifies the format of profile to be returned. + // If not specified, the profile will be returned in flame graph format. + Format ProfileFormat `protobuf:"varint,7,opt,name=format,proto3,enum=querier.v1.ProfileFormat" json:"format,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SelectMergeSpanProfileRequest) Reset() { + *x = SelectMergeSpanProfileRequest{} + mi := &file_querier_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SelectMergeSpanProfileRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SelectMergeSpanProfileRequest) ProtoMessage() {} + +func (x *SelectMergeSpanProfileRequest) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SelectMergeSpanProfileRequest.ProtoReflect.Descriptor instead. +func (*SelectMergeSpanProfileRequest) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{6} +} + +func (x *SelectMergeSpanProfileRequest) GetProfileTypeID() string { + if x != nil { + return x.ProfileTypeID + } + return "" +} + +func (x *SelectMergeSpanProfileRequest) GetLabelSelector() string { + if x != nil { + return x.LabelSelector + } + return "" +} + +func (x *SelectMergeSpanProfileRequest) GetSpanSelector() []string { + if x != nil { + return x.SpanSelector + } + return nil +} + +func (x *SelectMergeSpanProfileRequest) GetStart() int64 { + if x != nil { + return x.Start + } + return 0 +} + +func (x *SelectMergeSpanProfileRequest) GetEnd() int64 { + if x != nil { + return x.End + } + return 0 +} + +func (x *SelectMergeSpanProfileRequest) GetMaxNodes() int64 { + if x != nil && x.MaxNodes != nil { + return *x.MaxNodes + } + return 0 +} + +func (x *SelectMergeSpanProfileRequest) GetFormat() ProfileFormat { + if x != nil { + return x.Format + } + return ProfileFormat_PROFILE_FORMAT_UNSPECIFIED +} + +type SelectMergeSpanProfileResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Flamegraph *FlameGraph `protobuf:"bytes,1,opt,name=flamegraph,proto3" json:"flamegraph,omitempty"` + // Pyroscope tree bytes. + Tree []byte `protobuf:"bytes,2,opt,name=tree,proto3" json:"tree,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SelectMergeSpanProfileResponse) Reset() { + *x = SelectMergeSpanProfileResponse{} + mi := &file_querier_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SelectMergeSpanProfileResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SelectMergeSpanProfileResponse) ProtoMessage() {} + +func (x *SelectMergeSpanProfileResponse) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SelectMergeSpanProfileResponse.ProtoReflect.Descriptor instead. +func (*SelectMergeSpanProfileResponse) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{7} +} + +func (x *SelectMergeSpanProfileResponse) GetFlamegraph() *FlameGraph { + if x != nil { + return x.Flamegraph + } + return nil +} + +func (x *SelectMergeSpanProfileResponse) GetTree() []byte { + if x != nil { + return x.Tree + } + return nil +} + +type DiffRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Left *SelectMergeStacktracesRequest `protobuf:"bytes,1,opt,name=left,proto3" json:"left,omitempty"` + Right *SelectMergeStacktracesRequest `protobuf:"bytes,2,opt,name=right,proto3" json:"right,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DiffRequest) Reset() { + *x = DiffRequest{} + mi := &file_querier_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DiffRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DiffRequest) ProtoMessage() {} + +func (x *DiffRequest) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DiffRequest.ProtoReflect.Descriptor instead. +func (*DiffRequest) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{8} +} + +func (x *DiffRequest) GetLeft() *SelectMergeStacktracesRequest { + if x != nil { + return x.Left + } + return nil +} + +func (x *DiffRequest) GetRight() *SelectMergeStacktracesRequest { + if x != nil { + return x.Right + } + return nil +} + +type DiffResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Flamegraph *FlameGraphDiff `protobuf:"bytes,1,opt,name=flamegraph,proto3" json:"flamegraph,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DiffResponse) Reset() { + *x = DiffResponse{} + mi := &file_querier_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DiffResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DiffResponse) ProtoMessage() {} + +func (x *DiffResponse) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DiffResponse.ProtoReflect.Descriptor instead. +func (*DiffResponse) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{9} +} + +func (x *DiffResponse) GetFlamegraph() *FlameGraphDiff { + if x != nil { + return x.Flamegraph + } + return nil +} + +type FlameGraph struct { + state protoimpl.MessageState `protogen:"open.v1"` + Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` + Levels []*Level `protobuf:"bytes,2,rep,name=levels,proto3" json:"levels,omitempty"` + Total int64 `protobuf:"varint,3,opt,name=total,proto3" json:"total,omitempty"` + MaxSelf int64 `protobuf:"varint,4,opt,name=max_self,json=maxSelf,proto3" json:"max_self,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FlameGraph) Reset() { + *x = FlameGraph{} + mi := &file_querier_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FlameGraph) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FlameGraph) ProtoMessage() {} + +func (x *FlameGraph) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FlameGraph.ProtoReflect.Descriptor instead. +func (*FlameGraph) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{10} +} + +func (x *FlameGraph) GetNames() []string { + if x != nil { + return x.Names + } + return nil +} + +func (x *FlameGraph) GetLevels() []*Level { + if x != nil { + return x.Levels + } + return nil +} + +func (x *FlameGraph) GetTotal() int64 { + if x != nil { + return x.Total + } + return 0 +} + +func (x *FlameGraph) GetMaxSelf() int64 { + if x != nil { + return x.MaxSelf + } + return 0 +} + +type FlameGraphDiff struct { + state protoimpl.MessageState `protogen:"open.v1"` + Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` + Levels []*Level `protobuf:"bytes,2,rep,name=levels,proto3" json:"levels,omitempty"` + Total int64 `protobuf:"varint,3,opt,name=total,proto3" json:"total,omitempty"` + MaxSelf int64 `protobuf:"varint,4,opt,name=max_self,json=maxSelf,proto3" json:"max_self,omitempty"` + LeftTicks int64 `protobuf:"varint,5,opt,name=leftTicks,proto3" json:"leftTicks,omitempty"` + RightTicks int64 `protobuf:"varint,6,opt,name=rightTicks,proto3" json:"rightTicks,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FlameGraphDiff) Reset() { + *x = FlameGraphDiff{} + mi := &file_querier_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FlameGraphDiff) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FlameGraphDiff) ProtoMessage() {} + +func (x *FlameGraphDiff) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FlameGraphDiff.ProtoReflect.Descriptor instead. +func (*FlameGraphDiff) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{11} +} + +func (x *FlameGraphDiff) GetNames() []string { + if x != nil { + return x.Names + } + return nil +} + +func (x *FlameGraphDiff) GetLevels() []*Level { + if x != nil { + return x.Levels + } + return nil +} + +func (x *FlameGraphDiff) GetTotal() int64 { + if x != nil { + return x.Total + } + return 0 +} + +func (x *FlameGraphDiff) GetMaxSelf() int64 { + if x != nil { + return x.MaxSelf + } + return 0 +} + +func (x *FlameGraphDiff) GetLeftTicks() int64 { + if x != nil { + return x.LeftTicks + } + return 0 +} + +func (x *FlameGraphDiff) GetRightTicks() int64 { + if x != nil { + return x.RightTicks + } + return 0 +} + +type Level struct { + state protoimpl.MessageState `protogen:"open.v1"` + Values []int64 `protobuf:"varint,1,rep,packed,name=values,proto3" json:"values,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Level) Reset() { + *x = Level{} + mi := &file_querier_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Level) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Level) ProtoMessage() {} + +func (x *Level) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Level.ProtoReflect.Descriptor instead. +func (*Level) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{12} +} + +func (x *Level) GetValues() []int64 { + if x != nil { + return x.Values + } + return nil +} + +type SelectMergeProfileRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ProfileTypeID string `protobuf:"bytes,1,opt,name=profile_typeID,json=profileTypeID,proto3" json:"profile_typeID,omitempty"` + LabelSelector string `protobuf:"bytes,2,opt,name=label_selector,json=labelSelector,proto3" json:"label_selector,omitempty"` + // Milliseconds since epoch. + Start int64 `protobuf:"varint,3,opt,name=start,proto3" json:"start,omitempty"` + // Milliseconds since epoch. + End int64 `protobuf:"varint,4,opt,name=end,proto3" json:"end,omitempty"` + // Limit the nodes returned to only show the node with the max_node's biggest total + MaxNodes *int64 `protobuf:"varint,5,opt,name=max_nodes,json=maxNodes,proto3,oneof" json:"max_nodes,omitempty"` + // Select stack traces that match the provided selector. + StackTraceSelector *v1.StackTraceSelector `protobuf:"bytes,6,opt,name=stack_trace_selector,json=stackTraceSelector,proto3,oneof" json:"stack_trace_selector,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SelectMergeProfileRequest) Reset() { + *x = SelectMergeProfileRequest{} + mi := &file_querier_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SelectMergeProfileRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SelectMergeProfileRequest) ProtoMessage() {} + +func (x *SelectMergeProfileRequest) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SelectMergeProfileRequest.ProtoReflect.Descriptor instead. +func (*SelectMergeProfileRequest) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{13} +} + +func (x *SelectMergeProfileRequest) GetProfileTypeID() string { + if x != nil { + return x.ProfileTypeID + } + return "" +} + +func (x *SelectMergeProfileRequest) GetLabelSelector() string { + if x != nil { + return x.LabelSelector + } + return "" +} + +func (x *SelectMergeProfileRequest) GetStart() int64 { + if x != nil { + return x.Start + } + return 0 +} + +func (x *SelectMergeProfileRequest) GetEnd() int64 { + if x != nil { + return x.End + } + return 0 +} + +func (x *SelectMergeProfileRequest) GetMaxNodes() int64 { + if x != nil && x.MaxNodes != nil { + return *x.MaxNodes + } + return 0 +} + +func (x *SelectMergeProfileRequest) GetStackTraceSelector() *v1.StackTraceSelector { + if x != nil { + return x.StackTraceSelector + } + return nil +} + +type SelectSeriesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ProfileTypeID string `protobuf:"bytes,1,opt,name=profile_typeID,json=profileTypeID,proto3" json:"profile_typeID,omitempty"` + LabelSelector string `protobuf:"bytes,2,opt,name=label_selector,json=labelSelector,proto3" json:"label_selector,omitempty"` + // Milliseconds since epoch. + Start int64 `protobuf:"varint,3,opt,name=start,proto3" json:"start,omitempty"` + // Milliseconds since epoch. + End int64 `protobuf:"varint,4,opt,name=end,proto3" json:"end,omitempty"` + GroupBy []string `protobuf:"bytes,5,rep,name=group_by,json=groupBy,proto3" json:"group_by,omitempty"` + Step float64 `protobuf:"fixed64,6,opt,name=step,proto3" json:"step,omitempty"` + // Query resolution step width in seconds + Aggregation *v1.TimeSeriesAggregationType `protobuf:"varint,7,opt,name=aggregation,proto3,enum=types.v1.TimeSeriesAggregationType,oneof" json:"aggregation,omitempty"` + // Select stack traces that match the provided selector. + StackTraceSelector *v1.StackTraceSelector `protobuf:"bytes,8,opt,name=stack_trace_selector,json=stackTraceSelector,proto3,oneof" json:"stack_trace_selector,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SelectSeriesRequest) Reset() { + *x = SelectSeriesRequest{} + mi := &file_querier_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SelectSeriesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SelectSeriesRequest) ProtoMessage() {} + +func (x *SelectSeriesRequest) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SelectSeriesRequest.ProtoReflect.Descriptor instead. +func (*SelectSeriesRequest) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{14} +} + +func (x *SelectSeriesRequest) GetProfileTypeID() string { + if x != nil { + return x.ProfileTypeID + } + return "" +} + +func (x *SelectSeriesRequest) GetLabelSelector() string { + if x != nil { + return x.LabelSelector + } + return "" +} + +func (x *SelectSeriesRequest) GetStart() int64 { + if x != nil { + return x.Start + } + return 0 +} + +func (x *SelectSeriesRequest) GetEnd() int64 { + if x != nil { + return x.End + } + return 0 +} + +func (x *SelectSeriesRequest) GetGroupBy() []string { + if x != nil { + return x.GroupBy + } + return nil +} + +func (x *SelectSeriesRequest) GetStep() float64 { + if x != nil { + return x.Step + } + return 0 +} + +func (x *SelectSeriesRequest) GetAggregation() v1.TimeSeriesAggregationType { + if x != nil && x.Aggregation != nil { + return *x.Aggregation + } + return v1.TimeSeriesAggregationType(0) +} + +func (x *SelectSeriesRequest) GetStackTraceSelector() *v1.StackTraceSelector { + if x != nil { + return x.StackTraceSelector + } + return nil +} + +type SelectSeriesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Series []*v1.Series `protobuf:"bytes,1,rep,name=series,proto3" json:"series,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SelectSeriesResponse) Reset() { + *x = SelectSeriesResponse{} + mi := &file_querier_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SelectSeriesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SelectSeriesResponse) ProtoMessage() {} + +func (x *SelectSeriesResponse) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SelectSeriesResponse.ProtoReflect.Descriptor instead. +func (*SelectSeriesResponse) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{15} +} + +func (x *SelectSeriesResponse) GetSeries() []*v1.Series { + if x != nil { + return x.Series + } + return nil +} + +type AnalyzeQueryRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Start int64 `protobuf:"varint,2,opt,name=start,proto3" json:"start,omitempty"` + End int64 `protobuf:"varint,3,opt,name=end,proto3" json:"end,omitempty"` + Query string `protobuf:"bytes,4,opt,name=query,proto3" json:"query,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AnalyzeQueryRequest) Reset() { + *x = AnalyzeQueryRequest{} + mi := &file_querier_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AnalyzeQueryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AnalyzeQueryRequest) ProtoMessage() {} + +func (x *AnalyzeQueryRequest) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[16] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AnalyzeQueryRequest.ProtoReflect.Descriptor instead. +func (*AnalyzeQueryRequest) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{16} +} + +func (x *AnalyzeQueryRequest) GetStart() int64 { + if x != nil { + return x.Start + } + return 0 +} + +func (x *AnalyzeQueryRequest) GetEnd() int64 { + if x != nil { + return x.End + } + return 0 +} + +func (x *AnalyzeQueryRequest) GetQuery() string { + if x != nil { + return x.Query + } + return "" +} + +type AnalyzeQueryResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + QueryScopes []*QueryScope `protobuf:"bytes,1,rep,name=query_scopes,json=queryScopes,proto3" json:"query_scopes,omitempty"` // detailed view of what the query will require + QueryImpact *QueryImpact `protobuf:"bytes,2,opt,name=query_impact,json=queryImpact,proto3" json:"query_impact,omitempty"` // summary of the query impact / performance + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AnalyzeQueryResponse) Reset() { + *x = AnalyzeQueryResponse{} + mi := &file_querier_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AnalyzeQueryResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AnalyzeQueryResponse) ProtoMessage() {} + +func (x *AnalyzeQueryResponse) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AnalyzeQueryResponse.ProtoReflect.Descriptor instead. +func (*AnalyzeQueryResponse) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{17} +} + +func (x *AnalyzeQueryResponse) GetQueryScopes() []*QueryScope { + if x != nil { + return x.QueryScopes + } + return nil +} + +func (x *AnalyzeQueryResponse) GetQueryImpact() *QueryImpact { + if x != nil { + return x.QueryImpact + } + return nil +} + +type QueryScope struct { + state protoimpl.MessageState `protogen:"open.v1"` + ComponentType string `protobuf:"bytes,1,opt,name=component_type,json=componentType,proto3" json:"component_type,omitempty"` // a descriptive high level name of the component processing one part of the query (e.g., "short term storage") + ComponentCount uint64 `protobuf:"varint,2,opt,name=component_count,json=componentCount,proto3" json:"component_count,omitempty"` // how many components of this type will process the query (indicator of read-path replication) + BlockCount uint64 `protobuf:"varint,3,opt,name=block_count,json=blockCount,proto3" json:"block_count,omitempty"` + SeriesCount uint64 `protobuf:"varint,4,opt,name=series_count,json=seriesCount,proto3" json:"series_count,omitempty"` + ProfileCount uint64 `protobuf:"varint,5,opt,name=profile_count,json=profileCount,proto3" json:"profile_count,omitempty"` + SampleCount uint64 `protobuf:"varint,6,opt,name=sample_count,json=sampleCount,proto3" json:"sample_count,omitempty"` + IndexBytes uint64 `protobuf:"varint,7,opt,name=index_bytes,json=indexBytes,proto3" json:"index_bytes,omitempty"` + ProfileBytes uint64 `protobuf:"varint,8,opt,name=profile_bytes,json=profileBytes,proto3" json:"profile_bytes,omitempty"` + SymbolBytes uint64 `protobuf:"varint,9,opt,name=symbol_bytes,json=symbolBytes,proto3" json:"symbol_bytes,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *QueryScope) Reset() { + *x = QueryScope{} + mi := &file_querier_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *QueryScope) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryScope) ProtoMessage() {} + +func (x *QueryScope) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[18] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QueryScope.ProtoReflect.Descriptor instead. +func (*QueryScope) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{18} +} + +func (x *QueryScope) GetComponentType() string { + if x != nil { + return x.ComponentType + } + return "" +} + +func (x *QueryScope) GetComponentCount() uint64 { + if x != nil { + return x.ComponentCount + } + return 0 +} + +func (x *QueryScope) GetBlockCount() uint64 { + if x != nil { + return x.BlockCount + } + return 0 +} + +func (x *QueryScope) GetSeriesCount() uint64 { + if x != nil { + return x.SeriesCount + } + return 0 +} + +func (x *QueryScope) GetProfileCount() uint64 { + if x != nil { + return x.ProfileCount + } + return 0 +} + +func (x *QueryScope) GetSampleCount() uint64 { + if x != nil { + return x.SampleCount + } + return 0 +} + +func (x *QueryScope) GetIndexBytes() uint64 { + if x != nil { + return x.IndexBytes + } + return 0 +} + +func (x *QueryScope) GetProfileBytes() uint64 { + if x != nil { + return x.ProfileBytes + } + return 0 +} + +func (x *QueryScope) GetSymbolBytes() uint64 { + if x != nil { + return x.SymbolBytes + } + return 0 +} + +type QueryImpact struct { + state protoimpl.MessageState `protogen:"open.v1"` + TotalBytesInTimeRange uint64 `protobuf:"varint,2,opt,name=total_bytes_in_time_range,json=totalBytesInTimeRange,proto3" json:"total_bytes_in_time_range,omitempty"` + TotalQueriedSeries uint64 `protobuf:"varint,3,opt,name=total_queried_series,json=totalQueriedSeries,proto3" json:"total_queried_series,omitempty"` + DeduplicationNeeded bool `protobuf:"varint,4,opt,name=deduplication_needed,json=deduplicationNeeded,proto3" json:"deduplication_needed,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *QueryImpact) Reset() { + *x = QueryImpact{} + mi := &file_querier_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *QueryImpact) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryImpact) ProtoMessage() {} + +func (x *QueryImpact) ProtoReflect() protoreflect.Message { + mi := &file_querier_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QueryImpact.ProtoReflect.Descriptor instead. +func (*QueryImpact) Descriptor() ([]byte, []int) { + return file_querier_proto_rawDescGZIP(), []int{19} +} + +func (x *QueryImpact) GetTotalBytesInTimeRange() uint64 { + if x != nil { + return x.TotalBytesInTimeRange + } + return 0 +} + +func (x *QueryImpact) GetTotalQueriedSeries() uint64 { + if x != nil { + return x.TotalQueriedSeries + } + return 0 +} + +func (x *QueryImpact) GetDeduplicationNeeded() bool { + if x != nil { + return x.DeduplicationNeeded + } + return false +} + +var File_querier_proto protoreflect.FileDescriptor + +var file_querier_proto_rawDesc = string([]byte{ + 0x0a, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x0a, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x1a, 0x17, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3d, 0x0a, 0x13, 0x50, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x52, 0x0a, 0x14, 0x50, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x3a, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, + 0x0c, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x22, 0x74, 0x0a, + 0x0d, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x08, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x61, + 0x62, 0x65, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0a, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, + 0x65, 0x6e, 0x64, 0x22, 0x41, 0x0a, 0x0e, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x0a, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x5f, + 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x09, 0x6c, 0x61, 0x62, + 0x65, 0x6c, 0x73, 0x53, 0x65, 0x74, 0x22, 0xf8, 0x01, 0x0a, 0x1d, 0x53, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x44, 0x12, + 0x25, 0x0a, 0x0e, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x65, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x20, + 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x03, 0x48, 0x00, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x88, 0x01, 0x01, + 0x12, 0x31, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x19, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x06, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x6f, 0x64, 0x65, + 0x73, 0x22, 0x6c, 0x0a, 0x1e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x72, 0x67, 0x65, + 0x53, 0x74, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x0a, 0x66, 0x6c, 0x61, 0x6d, 0x65, 0x67, 0x72, 0x61, 0x70, + 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, + 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x61, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, + 0x0a, 0x66, 0x6c, 0x61, 0x6d, 0x65, 0x67, 0x72, 0x61, 0x70, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x72, 0x65, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, 0x22, + 0x9d, 0x02, 0x0a, 0x1d, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x53, + 0x70, 0x61, 0x6e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x44, 0x12, 0x25, 0x0a, 0x0e, 0x6c, 0x61, 0x62, 0x65, + 0x6c, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, + 0x23, 0x0a, 0x0d, 0x73, 0x70, 0x61, 0x6e, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x70, 0x61, 0x6e, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, + 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x20, 0x0a, 0x09, + 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x48, + 0x00, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x88, 0x01, 0x01, 0x12, 0x31, + 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, + 0x2e, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, + 0x6c, 0x0a, 0x1e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x53, 0x70, + 0x61, 0x6e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x36, 0x0a, 0x0a, 0x66, 0x6c, 0x61, 0x6d, 0x65, 0x67, 0x72, 0x61, 0x70, 0x68, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, + 0x76, 0x31, 0x2e, 0x46, 0x6c, 0x61, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x0a, 0x66, + 0x6c, 0x61, 0x6d, 0x65, 0x67, 0x72, 0x61, 0x70, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x72, 0x65, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, 0x22, 0x8d, 0x01, + 0x0a, 0x0b, 0x44, 0x69, 0x66, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3d, 0x0a, + 0x04, 0x6c, 0x65, 0x66, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x71, 0x75, + 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4d, + 0x65, 0x72, 0x67, 0x65, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x04, 0x6c, 0x65, 0x66, 0x74, 0x12, 0x3f, 0x0a, 0x05, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x71, 0x75, + 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4d, + 0x65, 0x72, 0x67, 0x65, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x05, 0x72, 0x69, 0x67, 0x68, 0x74, 0x22, 0x4a, 0x0a, + 0x0c, 0x44, 0x69, 0x66, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, + 0x0a, 0x66, 0x6c, 0x61, 0x6d, 0x65, 0x67, 0x72, 0x61, 0x70, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x46, + 0x6c, 0x61, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x44, 0x69, 0x66, 0x66, 0x52, 0x0a, 0x66, + 0x6c, 0x61, 0x6d, 0x65, 0x67, 0x72, 0x61, 0x70, 0x68, 0x22, 0x7e, 0x0a, 0x0a, 0x46, 0x6c, 0x61, + 0x6d, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x29, 0x0a, + 0x06, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, + 0x52, 0x06, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x19, + 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x6c, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x07, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x6c, 0x66, 0x22, 0xc0, 0x01, 0x0a, 0x0e, 0x46, 0x6c, + 0x61, 0x6d, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x44, 0x69, 0x66, 0x66, 0x12, 0x14, 0x0a, 0x05, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x06, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x12, 0x14, 0x0a, + 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x6c, 0x66, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x6c, 0x66, 0x12, 0x1c, + 0x0a, 0x09, 0x6c, 0x65, 0x66, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x09, 0x6c, 0x65, 0x66, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x73, 0x12, 0x1e, 0x0a, 0x0a, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0a, 0x72, 0x69, 0x67, 0x68, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x73, 0x22, 0x1f, 0x0a, 0x05, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x03, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0xaf, 0x02, + 0x0a, 0x19, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x50, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x49, 0x44, 0x12, 0x25, 0x0a, 0x0e, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x5f, 0x73, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6c, 0x61, 0x62, 0x65, + 0x6c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, + 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, + 0x64, 0x12, 0x20, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x4e, 0x6f, 0x64, 0x65, 0x73, + 0x88, 0x01, 0x01, 0x12, 0x53, 0x0a, 0x14, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x5f, 0x74, 0x72, 0x61, + 0x63, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, + 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x48, + 0x01, 0x52, 0x12, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x53, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d, 0x61, 0x78, + 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x73, 0x74, 0x61, 0x63, 0x6b, + 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, + 0x84, 0x03, 0x0a, 0x13, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x44, 0x12, 0x25, + 0x0a, 0x0e, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x53, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, + 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x19, 0x0a, + 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x62, 0x79, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x74, 0x65, 0x70, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x73, 0x74, 0x65, 0x70, 0x12, 0x4a, 0x0a, 0x0b, + 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x23, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x53, 0x0a, 0x14, 0x73, 0x74, 0x61, 0x63, + 0x6b, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x48, 0x01, 0x52, 0x12, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, + 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, + 0x0c, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x17, 0x0a, + 0x15, 0x5f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x40, 0x0a, 0x14, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, + 0x0a, 0x06, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x52, 0x06, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x22, 0x53, 0x0a, 0x13, 0x41, 0x6e, 0x61, 0x6c, + 0x79, 0x7a, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x8d, 0x01, + 0x0a, 0x14, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0c, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, + 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x71, + 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, + 0x63, 0x6f, 0x70, 0x65, 0x52, 0x0b, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x6f, 0x70, 0x65, + 0x73, 0x12, 0x3a, 0x0a, 0x0c, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x69, 0x6d, 0x70, 0x61, 0x63, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, + 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6d, 0x70, 0x61, 0x63, 0x74, + 0x52, 0x0b, 0x71, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x22, 0xd1, 0x02, + 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x0e, + 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, + 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x63, 0x6f, + 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x21, 0x0a, + 0x0c, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x21, + 0x0a, 0x0c, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x42, 0x79, 0x74, 0x65, + 0x73, 0x22, 0xac, 0x01, 0x0a, 0x0b, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6d, 0x70, 0x61, 0x63, + 0x74, 0x12, 0x38, 0x0a, 0x19, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x5f, 0x69, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x73, + 0x49, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x51, 0x75, 0x65, 0x72, 0x69, 0x65, 0x64, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x31, 0x0a, + 0x14, 0x64, 0x65, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, + 0x65, 0x65, 0x64, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x64, 0x65, 0x64, + 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x65, 0x65, 0x64, 0x65, 0x64, + 0x2a, 0x67, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x12, 0x1e, 0x0a, 0x1a, 0x50, 0x52, 0x4f, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x46, 0x4f, 0x52, + 0x4d, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x1d, 0x0a, 0x19, 0x50, 0x52, 0x4f, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x46, 0x4f, 0x52, + 0x4d, 0x41, 0x54, 0x5f, 0x46, 0x4c, 0x41, 0x4d, 0x45, 0x47, 0x52, 0x41, 0x50, 0x48, 0x10, 0x01, + 0x12, 0x17, 0x0a, 0x13, 0x50, 0x52, 0x4f, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x46, 0x4f, 0x52, 0x4d, + 0x41, 0x54, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x10, 0x02, 0x32, 0xbb, 0x07, 0x0a, 0x0e, 0x51, 0x75, + 0x65, 0x72, 0x69, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x53, 0x0a, 0x0c, + 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x71, + 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, + 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x4c, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x12, 0x1c, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, + 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x49, 0x0a, 0x0a, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1b, 0x2e, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x06, 0x53, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1a, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, + 0x16, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x53, 0x74, 0x61, 0x63, + 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x12, 0x29, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, + 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x72, 0x67, 0x65, + 0x53, 0x74, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x53, 0x74, 0x61, 0x63, 0x6b, + 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x71, 0x0a, 0x16, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x53, + 0x70, 0x61, 0x6e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x29, 0x2e, 0x71, 0x75, 0x65, + 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4d, 0x65, + 0x72, 0x67, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, + 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x53, 0x70, + 0x61, 0x6e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x12, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x72, + 0x67, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x25, 0x2e, 0x71, 0x75, 0x65, 0x72, + 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x72, + 0x67, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, + 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x69, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x04, 0x44, + 0x69, 0x66, 0x66, 0x12, 0x17, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x69, 0x66, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x71, + 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x66, 0x66, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0c, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, + 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x72, 0x2e, 0x76, 0x31, + 0x2e, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x25, 0x5a, 0x23, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x6f, 0x2f, 0x71, 0x72, + 0x79, 0x6e, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x66, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +}) + +var ( + file_querier_proto_rawDescOnce sync.Once + file_querier_proto_rawDescData []byte +) + +func file_querier_proto_rawDescGZIP() []byte { + file_querier_proto_rawDescOnce.Do(func() { + file_querier_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_querier_proto_rawDesc), len(file_querier_proto_rawDesc))) + }) + return file_querier_proto_rawDescData +} + +var file_querier_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_querier_proto_msgTypes = make([]protoimpl.MessageInfo, 20) +var file_querier_proto_goTypes = []any{ + (ProfileFormat)(0), // 0: querier.v1.ProfileFormat + (*ProfileTypesRequest)(nil), // 1: querier.v1.ProfileTypesRequest + (*ProfileTypesResponse)(nil), // 2: querier.v1.ProfileTypesResponse + (*SeriesRequest)(nil), // 3: querier.v1.SeriesRequest + (*SeriesResponse)(nil), // 4: querier.v1.SeriesResponse + (*SelectMergeStacktracesRequest)(nil), // 5: querier.v1.SelectMergeStacktracesRequest + (*SelectMergeStacktracesResponse)(nil), // 6: querier.v1.SelectMergeStacktracesResponse + (*SelectMergeSpanProfileRequest)(nil), // 7: querier.v1.SelectMergeSpanProfileRequest + (*SelectMergeSpanProfileResponse)(nil), // 8: querier.v1.SelectMergeSpanProfileResponse + (*DiffRequest)(nil), // 9: querier.v1.DiffRequest + (*DiffResponse)(nil), // 10: querier.v1.DiffResponse + (*FlameGraph)(nil), // 11: querier.v1.FlameGraph + (*FlameGraphDiff)(nil), // 12: querier.v1.FlameGraphDiff + (*Level)(nil), // 13: querier.v1.Level + (*SelectMergeProfileRequest)(nil), // 14: querier.v1.SelectMergeProfileRequest + (*SelectSeriesRequest)(nil), // 15: querier.v1.SelectSeriesRequest + (*SelectSeriesResponse)(nil), // 16: querier.v1.SelectSeriesResponse + (*AnalyzeQueryRequest)(nil), // 17: querier.v1.AnalyzeQueryRequest + (*AnalyzeQueryResponse)(nil), // 18: querier.v1.AnalyzeQueryResponse + (*QueryScope)(nil), // 19: querier.v1.QueryScope + (*QueryImpact)(nil), // 20: querier.v1.QueryImpact + (*v1.ProfileType)(nil), // 21: types.v1.ProfileType + (*v1.Labels)(nil), // 22: types.v1.Labels + (*v1.StackTraceSelector)(nil), // 23: types.v1.StackTraceSelector + (v1.TimeSeriesAggregationType)(0), // 24: types.v1.TimeSeriesAggregationType + (*v1.Series)(nil), // 25: types.v1.Series + (*v1.LabelValuesRequest)(nil), // 26: types.v1.LabelValuesRequest + (*v1.LabelNamesRequest)(nil), // 27: types.v1.LabelNamesRequest + (*v1.GetProfileStatsRequest)(nil), // 28: types.v1.GetProfileStatsRequest + (*v1.LabelValuesResponse)(nil), // 29: types.v1.LabelValuesResponse + (*v1.LabelNamesResponse)(nil), // 30: types.v1.LabelNamesResponse + (*v11.Profile)(nil), // 31: google.v1.Profile + (*v1.GetProfileStatsResponse)(nil), // 32: types.v1.GetProfileStatsResponse +} +var file_querier_proto_depIdxs = []int32{ + 21, // 0: querier.v1.ProfileTypesResponse.profile_types:type_name -> types.v1.ProfileType + 22, // 1: querier.v1.SeriesResponse.labels_set:type_name -> types.v1.Labels + 0, // 2: querier.v1.SelectMergeStacktracesRequest.format:type_name -> querier.v1.ProfileFormat + 11, // 3: querier.v1.SelectMergeStacktracesResponse.flamegraph:type_name -> querier.v1.FlameGraph + 0, // 4: querier.v1.SelectMergeSpanProfileRequest.format:type_name -> querier.v1.ProfileFormat + 11, // 5: querier.v1.SelectMergeSpanProfileResponse.flamegraph:type_name -> querier.v1.FlameGraph + 5, // 6: querier.v1.DiffRequest.left:type_name -> querier.v1.SelectMergeStacktracesRequest + 5, // 7: querier.v1.DiffRequest.right:type_name -> querier.v1.SelectMergeStacktracesRequest + 12, // 8: querier.v1.DiffResponse.flamegraph:type_name -> querier.v1.FlameGraphDiff + 13, // 9: querier.v1.FlameGraph.levels:type_name -> querier.v1.Level + 13, // 10: querier.v1.FlameGraphDiff.levels:type_name -> querier.v1.Level + 23, // 11: querier.v1.SelectMergeProfileRequest.stack_trace_selector:type_name -> types.v1.StackTraceSelector + 24, // 12: querier.v1.SelectSeriesRequest.aggregation:type_name -> types.v1.TimeSeriesAggregationType + 23, // 13: querier.v1.SelectSeriesRequest.stack_trace_selector:type_name -> types.v1.StackTraceSelector + 25, // 14: querier.v1.SelectSeriesResponse.series:type_name -> types.v1.Series + 19, // 15: querier.v1.AnalyzeQueryResponse.query_scopes:type_name -> querier.v1.QueryScope + 20, // 16: querier.v1.AnalyzeQueryResponse.query_impact:type_name -> querier.v1.QueryImpact + 1, // 17: querier.v1.QuerierService.ProfileTypes:input_type -> querier.v1.ProfileTypesRequest + 26, // 18: querier.v1.QuerierService.LabelValues:input_type -> types.v1.LabelValuesRequest + 27, // 19: querier.v1.QuerierService.LabelNames:input_type -> types.v1.LabelNamesRequest + 3, // 20: querier.v1.QuerierService.Series:input_type -> querier.v1.SeriesRequest + 5, // 21: querier.v1.QuerierService.SelectMergeStacktraces:input_type -> querier.v1.SelectMergeStacktracesRequest + 7, // 22: querier.v1.QuerierService.SelectMergeSpanProfile:input_type -> querier.v1.SelectMergeSpanProfileRequest + 14, // 23: querier.v1.QuerierService.SelectMergeProfile:input_type -> querier.v1.SelectMergeProfileRequest + 15, // 24: querier.v1.QuerierService.SelectSeries:input_type -> querier.v1.SelectSeriesRequest + 9, // 25: querier.v1.QuerierService.Diff:input_type -> querier.v1.DiffRequest + 28, // 26: querier.v1.QuerierService.GetProfileStats:input_type -> types.v1.GetProfileStatsRequest + 17, // 27: querier.v1.QuerierService.AnalyzeQuery:input_type -> querier.v1.AnalyzeQueryRequest + 2, // 28: querier.v1.QuerierService.ProfileTypes:output_type -> querier.v1.ProfileTypesResponse + 29, // 29: querier.v1.QuerierService.LabelValues:output_type -> types.v1.LabelValuesResponse + 30, // 30: querier.v1.QuerierService.LabelNames:output_type -> types.v1.LabelNamesResponse + 4, // 31: querier.v1.QuerierService.Series:output_type -> querier.v1.SeriesResponse + 6, // 32: querier.v1.QuerierService.SelectMergeStacktraces:output_type -> querier.v1.SelectMergeStacktracesResponse + 8, // 33: querier.v1.QuerierService.SelectMergeSpanProfile:output_type -> querier.v1.SelectMergeSpanProfileResponse + 31, // 34: querier.v1.QuerierService.SelectMergeProfile:output_type -> google.v1.Profile + 16, // 35: querier.v1.QuerierService.SelectSeries:output_type -> querier.v1.SelectSeriesResponse + 10, // 36: querier.v1.QuerierService.Diff:output_type -> querier.v1.DiffResponse + 32, // 37: querier.v1.QuerierService.GetProfileStats:output_type -> types.v1.GetProfileStatsResponse + 18, // 38: querier.v1.QuerierService.AnalyzeQuery:output_type -> querier.v1.AnalyzeQueryResponse + 28, // [28:39] is the sub-list for method output_type + 17, // [17:28] is the sub-list for method input_type + 17, // [17:17] is the sub-list for extension type_name + 17, // [17:17] is the sub-list for extension extendee + 0, // [0:17] is the sub-list for field type_name +} + +func init() { file_querier_proto_init() } +func file_querier_proto_init() { + if File_querier_proto != nil { + return + } + file_querier_proto_msgTypes[4].OneofWrappers = []any{} + file_querier_proto_msgTypes[6].OneofWrappers = []any{} + file_querier_proto_msgTypes[13].OneofWrappers = []any{} + file_querier_proto_msgTypes[14].OneofWrappers = []any{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_querier_proto_rawDesc), len(file_querier_proto_rawDesc)), + NumEnums: 1, + NumMessages: 20, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_querier_proto_goTypes, + DependencyIndexes: file_querier_proto_depIdxs, + EnumInfos: file_querier_proto_enumTypes, + MessageInfos: file_querier_proto_msgTypes, + }.Build() + File_querier_proto = out.File + file_querier_proto_goTypes = nil + file_querier_proto_depIdxs = nil +} diff --git a/reader/prof/querier_grpc.pb.go b/reader/prof/querier_grpc.pb.go new file mode 100644 index 00000000..e4af41ba --- /dev/null +++ b/reader/prof/querier_grpc.pb.go @@ -0,0 +1,523 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v3.21.12 +// source: querier.proto + +package prof + +import ( + context "context" + v11 "github.com/metrico/qryn/reader/prof/google/v1" + v1 "github.com/metrico/qryn/reader/prof/types/v1" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + QuerierService_ProfileTypes_FullMethodName = "/querier.v1.QuerierService/ProfileTypes" + QuerierService_LabelValues_FullMethodName = "/querier.v1.QuerierService/LabelValues" + QuerierService_LabelNames_FullMethodName = "/querier.v1.QuerierService/LabelNames" + QuerierService_Series_FullMethodName = "/querier.v1.QuerierService/Series" + QuerierService_SelectMergeStacktraces_FullMethodName = "/querier.v1.QuerierService/SelectMergeStacktraces" + QuerierService_SelectMergeSpanProfile_FullMethodName = "/querier.v1.QuerierService/SelectMergeSpanProfile" + QuerierService_SelectMergeProfile_FullMethodName = "/querier.v1.QuerierService/SelectMergeProfile" + QuerierService_SelectSeries_FullMethodName = "/querier.v1.QuerierService/SelectSeries" + QuerierService_Diff_FullMethodName = "/querier.v1.QuerierService/Diff" + QuerierService_GetProfileStats_FullMethodName = "/querier.v1.QuerierService/GetProfileStats" + QuerierService_AnalyzeQuery_FullMethodName = "/querier.v1.QuerierService/AnalyzeQuery" +) + +// QuerierServiceClient is the client API for QuerierService service. +// +// 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. +type QuerierServiceClient interface { + // ProfileType returns a list of the existing profile types. + ProfileTypes(ctx context.Context, in *ProfileTypesRequest, opts ...grpc.CallOption) (*ProfileTypesResponse, error) + // LabelValues returns the existing label values for the provided label names. + LabelValues(ctx context.Context, in *v1.LabelValuesRequest, opts ...grpc.CallOption) (*v1.LabelValuesResponse, error) + // LabelNames returns a list of the existing label names. + LabelNames(ctx context.Context, in *v1.LabelNamesRequest, opts ...grpc.CallOption) (*v1.LabelNamesResponse, error) + // Series returns profiles series matching the request. A series is a unique label set. + Series(ctx context.Context, in *SeriesRequest, opts ...grpc.CallOption) (*SeriesResponse, error) + // SelectMergeStacktraces returns matching profiles aggregated in a flamegraph format. It will combine samples from within the same callstack, with each element being grouped by its function name. + SelectMergeStacktraces(ctx context.Context, in *SelectMergeStacktracesRequest, opts ...grpc.CallOption) (*SelectMergeStacktracesResponse, error) + // SelectMergeSpanProfile returns matching profiles aggregated in a flamegraph format. It will combine samples from within the same callstack, with each element being grouped by its function name. + SelectMergeSpanProfile(ctx context.Context, in *SelectMergeSpanProfileRequest, opts ...grpc.CallOption) (*SelectMergeSpanProfileResponse, error) + // SelectMergeProfile returns matching profiles aggregated in pprof format. It will contain all information stored (so including filenames and line number, if ingested). + SelectMergeProfile(ctx context.Context, in *SelectMergeProfileRequest, opts ...grpc.CallOption) (*v11.Profile, error) + // SelectSeries returns a time series for the total sum of the requested profiles. + SelectSeries(ctx context.Context, in *SelectSeriesRequest, opts ...grpc.CallOption) (*SelectSeriesResponse, error) + // Diff returns a diff of two profiles + Diff(ctx context.Context, in *DiffRequest, opts ...grpc.CallOption) (*DiffResponse, error) + // GetProfileStats returns profile stats for the current tenant. + GetProfileStats(ctx context.Context, in *v1.GetProfileStatsRequest, opts ...grpc.CallOption) (*v1.GetProfileStatsResponse, error) + AnalyzeQuery(ctx context.Context, in *AnalyzeQueryRequest, opts ...grpc.CallOption) (*AnalyzeQueryResponse, error) +} + +type querierServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewQuerierServiceClient(cc grpc.ClientConnInterface) QuerierServiceClient { + return &querierServiceClient{cc} +} + +func (c *querierServiceClient) ProfileTypes(ctx context.Context, in *ProfileTypesRequest, opts ...grpc.CallOption) (*ProfileTypesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ProfileTypesResponse) + err := c.cc.Invoke(ctx, QuerierService_ProfileTypes_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *querierServiceClient) LabelValues(ctx context.Context, in *v1.LabelValuesRequest, opts ...grpc.CallOption) (*v1.LabelValuesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(v1.LabelValuesResponse) + err := c.cc.Invoke(ctx, QuerierService_LabelValues_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *querierServiceClient) LabelNames(ctx context.Context, in *v1.LabelNamesRequest, opts ...grpc.CallOption) (*v1.LabelNamesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(v1.LabelNamesResponse) + err := c.cc.Invoke(ctx, QuerierService_LabelNames_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *querierServiceClient) Series(ctx context.Context, in *SeriesRequest, opts ...grpc.CallOption) (*SeriesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SeriesResponse) + err := c.cc.Invoke(ctx, QuerierService_Series_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *querierServiceClient) SelectMergeStacktraces(ctx context.Context, in *SelectMergeStacktracesRequest, opts ...grpc.CallOption) (*SelectMergeStacktracesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SelectMergeStacktracesResponse) + err := c.cc.Invoke(ctx, QuerierService_SelectMergeStacktraces_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *querierServiceClient) SelectMergeSpanProfile(ctx context.Context, in *SelectMergeSpanProfileRequest, opts ...grpc.CallOption) (*SelectMergeSpanProfileResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SelectMergeSpanProfileResponse) + err := c.cc.Invoke(ctx, QuerierService_SelectMergeSpanProfile_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *querierServiceClient) SelectMergeProfile(ctx context.Context, in *SelectMergeProfileRequest, opts ...grpc.CallOption) (*v11.Profile, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(v11.Profile) + err := c.cc.Invoke(ctx, QuerierService_SelectMergeProfile_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *querierServiceClient) SelectSeries(ctx context.Context, in *SelectSeriesRequest, opts ...grpc.CallOption) (*SelectSeriesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SelectSeriesResponse) + err := c.cc.Invoke(ctx, QuerierService_SelectSeries_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *querierServiceClient) Diff(ctx context.Context, in *DiffRequest, opts ...grpc.CallOption) (*DiffResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(DiffResponse) + err := c.cc.Invoke(ctx, QuerierService_Diff_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *querierServiceClient) GetProfileStats(ctx context.Context, in *v1.GetProfileStatsRequest, opts ...grpc.CallOption) (*v1.GetProfileStatsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(v1.GetProfileStatsResponse) + err := c.cc.Invoke(ctx, QuerierService_GetProfileStats_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *querierServiceClient) AnalyzeQuery(ctx context.Context, in *AnalyzeQueryRequest, opts ...grpc.CallOption) (*AnalyzeQueryResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(AnalyzeQueryResponse) + err := c.cc.Invoke(ctx, QuerierService_AnalyzeQuery_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QuerierServiceServer is the server API for QuerierService service. +// All implementations must embed UnimplementedQuerierServiceServer +// for forward compatibility. +type QuerierServiceServer interface { + // ProfileType returns a list of the existing profile types. + ProfileTypes(context.Context, *ProfileTypesRequest) (*ProfileTypesResponse, error) + // LabelValues returns the existing label values for the provided label names. + LabelValues(context.Context, *v1.LabelValuesRequest) (*v1.LabelValuesResponse, error) + // LabelNames returns a list of the existing label names. + LabelNames(context.Context, *v1.LabelNamesRequest) (*v1.LabelNamesResponse, error) + // Series returns profiles series matching the request. A series is a unique label set. + Series(context.Context, *SeriesRequest) (*SeriesResponse, error) + // SelectMergeStacktraces returns matching profiles aggregated in a flamegraph format. It will combine samples from within the same callstack, with each element being grouped by its function name. + SelectMergeStacktraces(context.Context, *SelectMergeStacktracesRequest) (*SelectMergeStacktracesResponse, error) + // SelectMergeSpanProfile returns matching profiles aggregated in a flamegraph format. It will combine samples from within the same callstack, with each element being grouped by its function name. + SelectMergeSpanProfile(context.Context, *SelectMergeSpanProfileRequest) (*SelectMergeSpanProfileResponse, error) + // SelectMergeProfile returns matching profiles aggregated in pprof format. It will contain all information stored (so including filenames and line number, if ingested). + SelectMergeProfile(context.Context, *SelectMergeProfileRequest) (*v11.Profile, error) + // SelectSeries returns a time series for the total sum of the requested profiles. + SelectSeries(context.Context, *SelectSeriesRequest) (*SelectSeriesResponse, error) + // Diff returns a diff of two profiles + Diff(context.Context, *DiffRequest) (*DiffResponse, error) + // GetProfileStats returns profile stats for the current tenant. + GetProfileStats(context.Context, *v1.GetProfileStatsRequest) (*v1.GetProfileStatsResponse, error) + AnalyzeQuery(context.Context, *AnalyzeQueryRequest) (*AnalyzeQueryResponse, error) + mustEmbedUnimplementedQuerierServiceServer() +} + +// UnimplementedQuerierServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedQuerierServiceServer struct{} + +func (UnimplementedQuerierServiceServer) ProfileTypes(context.Context, *ProfileTypesRequest) (*ProfileTypesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ProfileTypes not implemented") +} +func (UnimplementedQuerierServiceServer) LabelValues(context.Context, *v1.LabelValuesRequest) (*v1.LabelValuesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method LabelValues not implemented") +} +func (UnimplementedQuerierServiceServer) LabelNames(context.Context, *v1.LabelNamesRequest) (*v1.LabelNamesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method LabelNames not implemented") +} +func (UnimplementedQuerierServiceServer) Series(context.Context, *SeriesRequest) (*SeriesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Series not implemented") +} +func (UnimplementedQuerierServiceServer) SelectMergeStacktraces(context.Context, *SelectMergeStacktracesRequest) (*SelectMergeStacktracesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SelectMergeStacktraces not implemented") +} +func (UnimplementedQuerierServiceServer) SelectMergeSpanProfile(context.Context, *SelectMergeSpanProfileRequest) (*SelectMergeSpanProfileResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SelectMergeSpanProfile not implemented") +} +func (UnimplementedQuerierServiceServer) SelectMergeProfile(context.Context, *SelectMergeProfileRequest) (*v11.Profile, error) { + return nil, status.Errorf(codes.Unimplemented, "method SelectMergeProfile not implemented") +} +func (UnimplementedQuerierServiceServer) SelectSeries(context.Context, *SelectSeriesRequest) (*SelectSeriesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SelectSeries not implemented") +} +func (UnimplementedQuerierServiceServer) Diff(context.Context, *DiffRequest) (*DiffResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Diff not implemented") +} +func (UnimplementedQuerierServiceServer) GetProfileStats(context.Context, *v1.GetProfileStatsRequest) (*v1.GetProfileStatsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetProfileStats not implemented") +} +func (UnimplementedQuerierServiceServer) AnalyzeQuery(context.Context, *AnalyzeQueryRequest) (*AnalyzeQueryResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AnalyzeQuery not implemented") +} +func (UnimplementedQuerierServiceServer) mustEmbedUnimplementedQuerierServiceServer() {} +func (UnimplementedQuerierServiceServer) testEmbeddedByValue() {} + +// UnsafeQuerierServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to QuerierServiceServer will +// result in compilation errors. +type UnsafeQuerierServiceServer interface { + mustEmbedUnimplementedQuerierServiceServer() +} + +func RegisterQuerierServiceServer(s grpc.ServiceRegistrar, srv QuerierServiceServer) { + // If the following call pancis, it indicates UnimplementedQuerierServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&QuerierService_ServiceDesc, srv) +} + +func _QuerierService_ProfileTypes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ProfileTypesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QuerierServiceServer).ProfileTypes(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: QuerierService_ProfileTypes_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QuerierServiceServer).ProfileTypes(ctx, req.(*ProfileTypesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _QuerierService_LabelValues_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(v1.LabelValuesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QuerierServiceServer).LabelValues(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: QuerierService_LabelValues_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QuerierServiceServer).LabelValues(ctx, req.(*v1.LabelValuesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _QuerierService_LabelNames_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(v1.LabelNamesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QuerierServiceServer).LabelNames(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: QuerierService_LabelNames_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QuerierServiceServer).LabelNames(ctx, req.(*v1.LabelNamesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _QuerierService_Series_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SeriesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QuerierServiceServer).Series(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: QuerierService_Series_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QuerierServiceServer).Series(ctx, req.(*SeriesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _QuerierService_SelectMergeStacktraces_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SelectMergeStacktracesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QuerierServiceServer).SelectMergeStacktraces(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: QuerierService_SelectMergeStacktraces_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QuerierServiceServer).SelectMergeStacktraces(ctx, req.(*SelectMergeStacktracesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _QuerierService_SelectMergeSpanProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SelectMergeSpanProfileRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QuerierServiceServer).SelectMergeSpanProfile(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: QuerierService_SelectMergeSpanProfile_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QuerierServiceServer).SelectMergeSpanProfile(ctx, req.(*SelectMergeSpanProfileRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _QuerierService_SelectMergeProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SelectMergeProfileRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QuerierServiceServer).SelectMergeProfile(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: QuerierService_SelectMergeProfile_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QuerierServiceServer).SelectMergeProfile(ctx, req.(*SelectMergeProfileRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _QuerierService_SelectSeries_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SelectSeriesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QuerierServiceServer).SelectSeries(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: QuerierService_SelectSeries_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QuerierServiceServer).SelectSeries(ctx, req.(*SelectSeriesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _QuerierService_Diff_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DiffRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QuerierServiceServer).Diff(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: QuerierService_Diff_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QuerierServiceServer).Diff(ctx, req.(*DiffRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _QuerierService_GetProfileStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(v1.GetProfileStatsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QuerierServiceServer).GetProfileStats(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: QuerierService_GetProfileStats_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QuerierServiceServer).GetProfileStats(ctx, req.(*v1.GetProfileStatsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _QuerierService_AnalyzeQuery_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AnalyzeQueryRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QuerierServiceServer).AnalyzeQuery(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: QuerierService_AnalyzeQuery_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QuerierServiceServer).AnalyzeQuery(ctx, req.(*AnalyzeQueryRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// QuerierService_ServiceDesc is the grpc.ServiceDesc for QuerierService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var QuerierService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "querier.v1.QuerierService", + HandlerType: (*QuerierServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ProfileTypes", + Handler: _QuerierService_ProfileTypes_Handler, + }, + { + MethodName: "LabelValues", + Handler: _QuerierService_LabelValues_Handler, + }, + { + MethodName: "LabelNames", + Handler: _QuerierService_LabelNames_Handler, + }, + { + MethodName: "Series", + Handler: _QuerierService_Series_Handler, + }, + { + MethodName: "SelectMergeStacktraces", + Handler: _QuerierService_SelectMergeStacktraces_Handler, + }, + { + MethodName: "SelectMergeSpanProfile", + Handler: _QuerierService_SelectMergeSpanProfile_Handler, + }, + { + MethodName: "SelectMergeProfile", + Handler: _QuerierService_SelectMergeProfile_Handler, + }, + { + MethodName: "SelectSeries", + Handler: _QuerierService_SelectSeries_Handler, + }, + { + MethodName: "Diff", + Handler: _QuerierService_Diff_Handler, + }, + { + MethodName: "GetProfileStats", + Handler: _QuerierService_GetProfileStats_Handler, + }, + { + MethodName: "AnalyzeQuery", + Handler: _QuerierService_AnalyzeQuery_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "querier.proto", +} diff --git a/reader/prof/settings.pb.go b/reader/prof/settings.pb.go new file mode 100644 index 00000000..df003eed --- /dev/null +++ b/reader/prof/settings.pb.go @@ -0,0 +1,350 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.4 +// protoc v3.21.12 +// source: settings.proto + +package prof + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type GetSettingsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetSettingsRequest) Reset() { + *x = GetSettingsRequest{} + mi := &file_settings_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetSettingsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSettingsRequest) ProtoMessage() {} + +func (x *GetSettingsRequest) ProtoReflect() protoreflect.Message { + mi := &file_settings_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSettingsRequest.ProtoReflect.Descriptor instead. +func (*GetSettingsRequest) Descriptor() ([]byte, []int) { + return file_settings_proto_rawDescGZIP(), []int{0} +} + +type GetSettingsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Settings []*Setting `protobuf:"bytes,1,rep,name=settings,proto3" json:"settings,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetSettingsResponse) Reset() { + *x = GetSettingsResponse{} + mi := &file_settings_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetSettingsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSettingsResponse) ProtoMessage() {} + +func (x *GetSettingsResponse) ProtoReflect() protoreflect.Message { + mi := &file_settings_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSettingsResponse.ProtoReflect.Descriptor instead. +func (*GetSettingsResponse) Descriptor() ([]byte, []int) { + return file_settings_proto_rawDescGZIP(), []int{1} +} + +func (x *GetSettingsResponse) GetSettings() []*Setting { + if x != nil { + return x.Settings + } + return nil +} + +type SetSettingsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Setting *Setting `protobuf:"bytes,1,opt,name=setting,proto3" json:"setting,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SetSettingsRequest) Reset() { + *x = SetSettingsRequest{} + mi := &file_settings_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SetSettingsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetSettingsRequest) ProtoMessage() {} + +func (x *SetSettingsRequest) ProtoReflect() protoreflect.Message { + mi := &file_settings_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetSettingsRequest.ProtoReflect.Descriptor instead. +func (*SetSettingsRequest) Descriptor() ([]byte, []int) { + return file_settings_proto_rawDescGZIP(), []int{2} +} + +func (x *SetSettingsRequest) GetSetting() *Setting { + if x != nil { + return x.Setting + } + return nil +} + +type SetSettingsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Setting *Setting `protobuf:"bytes,1,opt,name=setting,proto3" json:"setting,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SetSettingsResponse) Reset() { + *x = SetSettingsResponse{} + mi := &file_settings_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SetSettingsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetSettingsResponse) ProtoMessage() {} + +func (x *SetSettingsResponse) ProtoReflect() protoreflect.Message { + mi := &file_settings_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetSettingsResponse.ProtoReflect.Descriptor instead. +func (*SetSettingsResponse) Descriptor() ([]byte, []int) { + return file_settings_proto_rawDescGZIP(), []int{3} +} + +func (x *SetSettingsResponse) GetSetting() *Setting { + if x != nil { + return x.Setting + } + return nil +} + +type Setting struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + ModifiedAt int64 `protobuf:"varint,3,opt,name=modifiedAt,proto3" json:"modifiedAt,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Setting) Reset() { + *x = Setting{} + mi := &file_settings_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Setting) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Setting) ProtoMessage() {} + +func (x *Setting) ProtoReflect() protoreflect.Message { + mi := &file_settings_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Setting.ProtoReflect.Descriptor instead. +func (*Setting) Descriptor() ([]byte, []int) { + return file_settings_proto_rawDescGZIP(), []int{4} +} + +func (x *Setting) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Setting) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +func (x *Setting) GetModifiedAt() int64 { + if x != nil { + return x.ModifiedAt + } + return 0 +} + +var File_settings_proto protoreflect.FileDescriptor + +var file_settings_proto_rawDesc = string([]byte{ + 0x0a, 0x0e, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x22, 0x14, 0x0a, + 0x12, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x22, 0x47, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x08, 0x73, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x73, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x44, 0x0a, 0x12, + 0x53, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x22, 0x45, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x73, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x73, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x52, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x53, 0x0a, 0x07, 0x53, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1e, + 0x0a, 0x0a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x41, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x41, 0x74, 0x32, 0xa9, + 0x01, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x73, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x73, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4a, + 0x0a, 0x03, 0x53, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x25, 0x5a, 0x23, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x6f, + 0x2f, 0x71, 0x72, 0x79, 0x6e, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, + 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +}) + +var ( + file_settings_proto_rawDescOnce sync.Once + file_settings_proto_rawDescData []byte +) + +func file_settings_proto_rawDescGZIP() []byte { + file_settings_proto_rawDescOnce.Do(func() { + file_settings_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_settings_proto_rawDesc), len(file_settings_proto_rawDesc))) + }) + return file_settings_proto_rawDescData +} + +var file_settings_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_settings_proto_goTypes = []any{ + (*GetSettingsRequest)(nil), // 0: settings.v1.GetSettingsRequest + (*GetSettingsResponse)(nil), // 1: settings.v1.GetSettingsResponse + (*SetSettingsRequest)(nil), // 2: settings.v1.SetSettingsRequest + (*SetSettingsResponse)(nil), // 3: settings.v1.SetSettingsResponse + (*Setting)(nil), // 4: settings.v1.Setting +} +var file_settings_proto_depIdxs = []int32{ + 4, // 0: settings.v1.GetSettingsResponse.settings:type_name -> settings.v1.Setting + 4, // 1: settings.v1.SetSettingsRequest.setting:type_name -> settings.v1.Setting + 4, // 2: settings.v1.SetSettingsResponse.setting:type_name -> settings.v1.Setting + 0, // 3: settings.v1.SettingsService.Get:input_type -> settings.v1.GetSettingsRequest + 2, // 4: settings.v1.SettingsService.Set:input_type -> settings.v1.SetSettingsRequest + 1, // 5: settings.v1.SettingsService.Get:output_type -> settings.v1.GetSettingsResponse + 3, // 6: settings.v1.SettingsService.Set:output_type -> settings.v1.SetSettingsResponse + 5, // [5:7] is the sub-list for method output_type + 3, // [3:5] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_settings_proto_init() } +func file_settings_proto_init() { + if File_settings_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_settings_proto_rawDesc), len(file_settings_proto_rawDesc)), + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_settings_proto_goTypes, + DependencyIndexes: file_settings_proto_depIdxs, + MessageInfos: file_settings_proto_msgTypes, + }.Build() + File_settings_proto = out.File + file_settings_proto_goTypes = nil + file_settings_proto_depIdxs = nil +} diff --git a/reader/prof/settings_grpc.pb.go b/reader/prof/settings_grpc.pb.go new file mode 100644 index 00000000..1e2cda00 --- /dev/null +++ b/reader/prof/settings_grpc.pb.go @@ -0,0 +1,159 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v3.21.12 +// source: settings.proto + +package prof + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + SettingsService_Get_FullMethodName = "/settings.v1.SettingsService/Get" + SettingsService_Set_FullMethodName = "/settings.v1.SettingsService/Set" +) + +// SettingsServiceClient is the client API for SettingsService service. +// +// 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. +type SettingsServiceClient interface { + Get(ctx context.Context, in *GetSettingsRequest, opts ...grpc.CallOption) (*GetSettingsResponse, error) + Set(ctx context.Context, in *SetSettingsRequest, opts ...grpc.CallOption) (*SetSettingsResponse, error) +} + +type settingsServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewSettingsServiceClient(cc grpc.ClientConnInterface) SettingsServiceClient { + return &settingsServiceClient{cc} +} + +func (c *settingsServiceClient) Get(ctx context.Context, in *GetSettingsRequest, opts ...grpc.CallOption) (*GetSettingsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetSettingsResponse) + err := c.cc.Invoke(ctx, SettingsService_Get_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *settingsServiceClient) Set(ctx context.Context, in *SetSettingsRequest, opts ...grpc.CallOption) (*SetSettingsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SetSettingsResponse) + err := c.cc.Invoke(ctx, SettingsService_Set_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// SettingsServiceServer is the server API for SettingsService service. +// All implementations must embed UnimplementedSettingsServiceServer +// for forward compatibility. +type SettingsServiceServer interface { + Get(context.Context, *GetSettingsRequest) (*GetSettingsResponse, error) + Set(context.Context, *SetSettingsRequest) (*SetSettingsResponse, error) + mustEmbedUnimplementedSettingsServiceServer() +} + +// UnimplementedSettingsServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedSettingsServiceServer struct{} + +func (UnimplementedSettingsServiceServer) Get(context.Context, *GetSettingsRequest) (*GetSettingsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") +} +func (UnimplementedSettingsServiceServer) Set(context.Context, *SetSettingsRequest) (*SetSettingsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Set not implemented") +} +func (UnimplementedSettingsServiceServer) mustEmbedUnimplementedSettingsServiceServer() {} +func (UnimplementedSettingsServiceServer) testEmbeddedByValue() {} + +// UnsafeSettingsServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to SettingsServiceServer will +// result in compilation errors. +type UnsafeSettingsServiceServer interface { + mustEmbedUnimplementedSettingsServiceServer() +} + +func RegisterSettingsServiceServer(s grpc.ServiceRegistrar, srv SettingsServiceServer) { + // If the following call pancis, it indicates UnimplementedSettingsServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&SettingsService_ServiceDesc, srv) +} + +func _SettingsService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetSettingsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SettingsServiceServer).Get(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SettingsService_Get_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SettingsServiceServer).Get(ctx, req.(*GetSettingsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SettingsService_Set_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetSettingsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SettingsServiceServer).Set(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SettingsService_Set_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SettingsServiceServer).Set(ctx, req.(*SetSettingsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// SettingsService_ServiceDesc is the grpc.ServiceDesc for SettingsService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var SettingsService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "settings.v1.SettingsService", + HandlerType: (*SettingsServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Get", + Handler: _SettingsService_Get_Handler, + }, + { + MethodName: "Set", + Handler: _SettingsService_Set_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "settings.proto", +} diff --git a/reader/prof/shared/types.go b/reader/prof/shared/types.go new file mode 100644 index 00000000..49be86ce --- /dev/null +++ b/reader/prof/shared/types.go @@ -0,0 +1,44 @@ +package shared + +import ( + "errors" + "strings" +) + +var ( + ErrInvalidTypeId = errors.New("invalid type ID format") +) + +type TypeId struct { + Tp string + SampleType string + SampleUnit string + PeriodType string + PeriodUnit string +} + +func ParseTypeId(strTypeId string) (TypeId, error) { + parts := strings.SplitN(strTypeId, ":", 5) + if len(parts) != 5 { + return TypeId{}, ErrInvalidTypeId + } + return TypeId{ + Tp: parts[0], + SampleType: parts[1], + SampleUnit: parts[2], + PeriodType: parts[3], + PeriodUnit: parts[4], + }, nil +} + +func ParseShortTypeId(strTypeId string) (TypeId, error) { + parts := strings.SplitN(strTypeId, ":", 3) + if len(parts) != 3 { + return TypeId{}, ErrInvalidTypeId + } + return TypeId{ + Tp: parts[0], + PeriodType: parts[1], + PeriodUnit: parts[2], + }, nil +} diff --git a/reader/prof/transpiler/planner_distinct_time_series.go b/reader/prof/transpiler/planner_distinct_time_series.go new file mode 100644 index 00000000..af9569e8 --- /dev/null +++ b/reader/prof/transpiler/planner_distinct_time_series.go @@ -0,0 +1,27 @@ +package transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type TimeSeriesDistinctPlanner struct { + /* TimeSeriesSelectPlanner or union */ + Main shared.SQLRequestPlanner +} + +func (t TimeSeriesDistinctPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := t.Main.Process(ctx) + if err != nil { + return nil, err + } + + withMain := sql.NewWith(main, "pre_distinct") + return sql.NewSelect(). + With(withMain). + Distinct(true). + Select(sql.NewSimpleCol("tags", "tags"), + sql.NewSimpleCol("type_id", "type_id"), + sql.NewSimpleCol("__sample_types_units", "__sample_types_units")). + From(sql.NewWithRef(withMain)), nil +} diff --git a/reader/prof/transpiler/planner_filter_labels.go b/reader/prof/transpiler/planner_filter_labels.go new file mode 100644 index 00000000..cb5945a1 --- /dev/null +++ b/reader/prof/transpiler/planner_filter_labels.go @@ -0,0 +1,47 @@ +package transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type FilterLabelsPlanner struct { + /* SelectTimeSeriesPlanner */ + Main shared.SQLRequestPlanner + Labels []string +} + +func (f *FilterLabelsPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := f.Main.Process(ctx) + if err != nil { + return nil, err + } + if len(f.Labels) == 0 { + return main, nil + } + + withMain := sql.NewWith(main, "pre_label_filter") + + filterTagsCol := sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + sqlLabels := make([]sql.SQLObject, len(f.Labels)) + for i, label := range f.Labels { + sqlLabels[i] = sql.NewStringVal(label) + } + cond := sql.NewIn(sql.NewRawObject("x.1"), sqlLabels...) + strCond, err := cond.String(ctx, options...) + if err != nil { + return "", err + } + + return fmt.Sprintf("arrayFilter(x -> %s, tags)", strCond), nil + }) + + res := sql.NewSelect(). + With(withMain). + Select(sql.NewCol(filterTagsCol, "tags"), + sql.NewSimpleCol("type_id", "type_id"), + sql.NewSimpleCol("__sample_types_units", "__sample_types_units")). + From(sql.NewWithRef(withMain)) + return res, nil +} diff --git a/reader/prof/transpiler/planner_get_labels.go b/reader/prof/transpiler/planner_get_labels.go new file mode 100644 index 00000000..1f3f3d19 --- /dev/null +++ b/reader/prof/transpiler/planner_get_labels.go @@ -0,0 +1,63 @@ +package transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/clickhouse_planner" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/prof/parser" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type GetLabelsPlanner struct { + FP shared.SQLRequestPlanner + GroupBy []string + Selectors []parser.Selector +} + +func (g *GetLabelsPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + fp, err := g.FP.Process(ctx) + if err != nil { + return nil, err + } + + matchers, err := (&StreamSelectorPlanner{Selectors: g.Selectors}).getMatchers() + if err != nil { + return nil, err + } + + newFpCol := sql.NewSimpleCol("fingerprint", "new_fingerprint") + tagsCol := sql.NewSimpleCol("arraySort(p.tags)", "tags") + if len(g.GroupBy) > 0 { + newFpCol = sql.NewSimpleCol("cityHash64(tags)", "new_fingerprint") + tagsCol = sql.NewCol(sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + sqlGroupBy := make([]sql.SQLObject, len(g.GroupBy)) + for i, col := range g.GroupBy { + sqlGroupBy[i] = sql.NewStringVal(col) + } + inTags := sql.NewIn(sql.NewRawObject("x.1"), sqlGroupBy...) + strInTags, err := inTags.String(ctx, options...) + if err != nil { + return "", err + } + return fmt.Sprintf("arrayFilter(x -> %s, p.tags)", strInTags), nil + }), "tags") + } + + withFp := sql.NewWith(fp, "fp") + main := sql.NewSelect(). + With(withFp). + Distinct(true). + Select( + sql.NewRawObject("fingerprint"), + tagsCol, + newFpCol). + From(sql.NewCol(sql.NewRawObject(ctx.ProfilesSeriesTable), "p")). + AndWhere( + sql.NewIn(sql.NewRawObject("fingerprint"), sql.NewWithRef(withFp)), + sql.Ge(sql.NewRawObject("date"), sql.NewStringVal(clickhouse_planner.FormatFromDate(ctx.From))), + sql.Le(sql.NewRawObject("date"), sql.NewStringVal(clickhouse_planner.FormatFromDate(ctx.To)))) + if len(matchers.globalMatchers) > 0 { + main = main.AndWhere(matchers.globalMatchers...) + } + return main, nil +} diff --git a/reader/prof/transpiler/planner_label_generic.go b/reader/prof/transpiler/planner_label_generic.go new file mode 100644 index 00000000..4f283d39 --- /dev/null +++ b/reader/prof/transpiler/planner_label_generic.go @@ -0,0 +1,37 @@ +package transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/clickhouse_planner" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type GenericLabelsPlanner struct { + Fingerprints shared.SQLRequestPlanner +} + +func (l *GenericLabelsPlanner) _process(ctx *shared.PlannerContext, returnCol string) (sql.ISelect, error) { + var ( + fpReq sql.ISelect + err error + ) + if l.Fingerprints != nil { + fpReq, err = l.Fingerprints.Process(ctx) + if err != nil { + return nil, err + } + } + withFpReq := sql.NewWith(fpReq, "fp") + res := sql.NewSelect(). + Distinct(true). + Select(sql.NewRawObject(returnCol)). + From(sql.NewRawObject(ctx.ProfilesSeriesGinDistTable)). + AndWhere( + sql.Ge(sql.NewRawObject("date"), sql.NewStringVal(clickhouse_planner.FormatFromDate(ctx.From))), + sql.Le(sql.NewRawObject("date"), sql.NewStringVal(clickhouse_planner.FormatFromDate(ctx.To)))). + Limit(sql.NewIntVal(10000)) + if fpReq != nil { + res = res.With(withFpReq).AndWhere(sql.NewIn(sql.NewRawObject("fingerprint"), sql.NewWithRef(withFpReq))) + } + return res, nil +} diff --git a/reader/prof/transpiler/planner_label_names.go b/reader/prof/transpiler/planner_label_names.go new file mode 100644 index 00000000..d74cbb97 --- /dev/null +++ b/reader/prof/transpiler/planner_label_names.go @@ -0,0 +1,14 @@ +package transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type LabelNamesPlanner struct { + GenericLabelsPlanner +} + +func (l *LabelNamesPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + return l._process(ctx, "key") +} diff --git a/reader/prof/transpiler/planner_label_values.go b/reader/prof/transpiler/planner_label_values.go new file mode 100644 index 00000000..c00ea3a1 --- /dev/null +++ b/reader/prof/transpiler/planner_label_values.go @@ -0,0 +1,20 @@ +package transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type LabelValuesPlanner struct { + GenericLabelsPlanner + Label string +} + +func (l *LabelValuesPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + sel, err := l._process(ctx, "val") + if err != nil { + return nil, err + } + sel = sel.AndWhere(sql.Eq(sql.NewRawObject("key"), sql.NewStringVal(l.Label))) + return sel, nil +} diff --git a/reader/prof/transpiler/planner_merge_aggregated.go b/reader/prof/transpiler/planner_merge_aggregated.go new file mode 100644 index 00000000..42672c97 --- /dev/null +++ b/reader/prof/transpiler/planner_merge_aggregated.go @@ -0,0 +1,25 @@ +package transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type MergeAggregatedPlanner struct { + // MergeJoinedPlanner, potentially having "WITH raw as (MergeRawPlanner)" + Main shared.SQLRequestPlanner +} + +func (m *MergeAggregatedPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := m.Main.Process(ctx) + if err != nil { + return nil, err + } + withMain := sql.NewWith(main, "joined") + res := sql.NewSelect(). + With(withMain). + Select( + sql.NewSimpleCol("(select groupArray(tree) from joined)", "_tree"), + sql.NewSimpleCol("(select groupUniqArrayArray(functions) from raw )", "_functions")) + return res, nil +} diff --git a/reader/prof/transpiler/planner_merge_joined.go b/reader/prof/transpiler/planner_merge_joined.go new file mode 100644 index 00000000..b3fd5d26 --- /dev/null +++ b/reader/prof/transpiler/planner_merge_joined.go @@ -0,0 +1,38 @@ +package transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type MergeJoinedPlanner struct { + Main shared.SQLRequestPlanner +} + +func (j *MergeJoinedPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := j.Main.Process(ctx) + if err != nil { + return nil, err + } + withMain := sql.NewWith(main, "raw") + preJoined := sql.NewSelect(). + With(withMain). + Select(sql.NewRawObject("rtree")). + From(sql.NewWithRef(withMain)). + Join(sql.NewJoin("array", sql.NewSimpleCol("raw.tree", "rtree"), nil)) + withPreJoined := sql.NewWith(preJoined, "pre_joined") + res := sql.NewSelect(). + With(withPreJoined). + Select( + sql.NewSimpleCol( + "(rtree.1, rtree.2, rtree.3, sum(rtree.4), sum(rtree.5))", + "tree")). + From(sql.NewWithRef(withPreJoined)). + GroupBy( + sql.NewRawObject("rtree.1"), + sql.NewRawObject("rtree.2"), + sql.NewRawObject("rtree.3")). + OrderBy(sql.NewRawObject("rtree.1")). + Limit(sql.NewIntVal(2000000)) + return res, nil +} diff --git a/reader/prof/transpiler/planner_merge_profiles.go b/reader/prof/transpiler/planner_merge_profiles.go new file mode 100644 index 00000000..8a7ef9bb --- /dev/null +++ b/reader/prof/transpiler/planner_merge_profiles.go @@ -0,0 +1,42 @@ +package transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/prof/parser" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type MergeProfilesPlanner struct { + Fingerprints shared.SQLRequestPlanner + Selectors []parser.Selector +} + +func (m *MergeProfilesPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + fp, err := m.Fingerprints.Process(ctx) + if err != nil { + return nil, err + } + + matchers, err := (&StreamSelectorPlanner{Selectors: m.Selectors}).getMatchers() + if err != nil { + return nil, err + } + + withFpSel := sql.NewWith(fp, "fp") + main := sql.NewSelect(). + With(withFpSel). + Select(sql.NewRawObject("payload")). + From(sql.NewRawObject(ctx.ProfilesDistTable)). + AndWhere( + sql.Ge(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(ctx.From.UnixNano())), + sql.Le(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(ctx.To.UnixNano())), + sql.NewIn(sql.NewRawObject("fingerprint"), sql.NewWithRef(withFpSel))) + if len(matchers.globalMatchers) > 0 { + main.AndWhere(matchers.globalMatchers...) + } + if ctx.Limit != 0 { + main.OrderBy(sql.NewOrderBy(sql.NewRawObject("timestamp_ns"), sql.ORDER_BY_DIRECTION_DESC)). + Limit(sql.NewIntVal(ctx.Limit)) + } + return main, nil +} diff --git a/reader/prof/transpiler/planner_merge_raw.go b/reader/prof/transpiler/planner_merge_raw.go new file mode 100644 index 00000000..c5b70b39 --- /dev/null +++ b/reader/prof/transpiler/planner_merge_raw.go @@ -0,0 +1,52 @@ +package transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/prof/parser" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type MergeRawPlanner struct { + Fingerprints shared.SQLRequestPlanner + selectors []parser.Selector + sampleType string + sampleUnit string +} + +func (m *MergeRawPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + fpSel, err := m.Fingerprints.Process(ctx) + if err != nil { + return nil, err + } + matchers, err := (&StreamSelectorPlanner{Selectors: m.selectors}).getMatchers() + if err != nil { + return nil, err + } + withFpSel := sql.NewWith(fpSel, "fp") + main := sql.NewSelect(). + With(withFpSel). + Select( + sql.NewCol(sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + val := sql.NewStringVal(m.sampleType + ":" + m.sampleUnit) + strVal, err := val.String(ctx, options...) + if err != nil { + return "", err + } + return fmt.Sprintf( + "arrayMap(x -> (x.1, x.2, x.3, (arrayFirst(y -> y.1 == %s, x.4) as af).2, af.3), tree)", + strVal), nil + }), "tree"), + sql.NewRawObject("functions")). + From(sql.NewRawObject(ctx.ProfilesDistTable)). + AndWhere( + sql.Ge(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(ctx.From.UnixNano())), + sql.Lt(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(ctx.To.UnixNano())), + sql.NewIn(sql.NewRawObject("fingerprint"), sql.NewWithRef(withFpSel)), + sql.And(matchers.globalMatchers...)) + if ctx.Limit != 0 { + main.OrderBy(sql.NewOrderBy(sql.NewRawObject("timestamp_ns"), sql.ORDER_BY_DIRECTION_DESC)). + Limit(sql.NewIntVal(ctx.Limit)) + } + return main, nil +} diff --git a/reader/prof/transpiler/planner_profiles_size.go b/reader/prof/transpiler/planner_profiles_size.go new file mode 100644 index 00000000..6d2af480 --- /dev/null +++ b/reader/prof/transpiler/planner_profiles_size.go @@ -0,0 +1,51 @@ +package transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type ProfileSizePlanner struct { + /* MergeProfilesPlanner */ + Main shared.SQLRequestPlanner +} + +func (p *ProfileSizePlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := p.Main.Process(ctx) + if err != nil { + return nil, err + } + + withMain := sql.NewWith(main, "pre_profile_size") + selectProfileSize := sql.NewSelect(). + Select(sql.NewRawObject("sum(length(payload)::Int64)")). + From(sql.NewWithRef(withMain)) + var withFP *sql.With + for _, with := range main.GetWith() { + if with.GetAlias() == "fp" { + withFP = with + break + } + } + + selectFPCount := sql.NewSelect(). + Select(sql.NewRawObject("uniqExact(fingerprint)::Int64")). + From(sql.NewWithRef(withFP)) + + brackets := func(o sql.SQLObject) sql.SQLObject { + return sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + str, err := o.String(ctx, options...) + if err != nil { + return "", err + } + return fmt.Sprintf("(%s)", str), nil + }) + } + + res := sql.NewSelect(). + With(withMain). + Select(sql.NewCol(brackets(selectProfileSize), "profile_size"), + sql.NewCol(brackets(selectFPCount), "fingerprint_count")) + return res, nil +} diff --git a/reader/prof/transpiler/planner_select_all_time_series.go b/reader/prof/transpiler/planner_select_all_time_series.go new file mode 100644 index 00000000..3188492e --- /dev/null +++ b/reader/prof/transpiler/planner_select_all_time_series.go @@ -0,0 +1,24 @@ +package transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/clickhouse_planner" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type AllTimeSeriesSelectPlanner struct { +} + +func (s *AllTimeSeriesSelectPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + res := sql.NewSelect(). + Distinct(true). + Select(sql.NewSimpleCol("tags", "tags"), + sql.NewSimpleCol("type_id", "type_id"), + sql.NewSimpleCol("_sample_types_units", "__sample_types_units")). + From(sql.NewSimpleCol(ctx.ProfilesSeriesDistTable, "p")). + Join(sql.NewJoin("array", sql.NewSimpleCol("sample_types_units", "_sample_types_units"), nil)). + AndWhere( + sql.Ge(sql.NewRawObject("date"), sql.NewStringVal(clickhouse_planner.FormatFromDate(ctx.From))), + sql.Le(sql.NewRawObject("date"), sql.NewStringVal(clickhouse_planner.FormatFromDate(ctx.To)))) + return res, nil +} diff --git a/reader/prof/transpiler/planner_select_series.go b/reader/prof/transpiler/planner_select_series.go new file mode 100644 index 00000000..d3cc82b2 --- /dev/null +++ b/reader/prof/transpiler/planner_select_series.go @@ -0,0 +1,85 @@ +package transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/prof/parser" + v1 "github.com/metrico/qryn/reader/prof/types/v1" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type SelectSeriesPlanner struct { + GetLabelsPlanner shared.SQLRequestPlanner + Selectors []parser.Selector + SampleType string + SampleUnit string + Aggregation v1.TimeSeriesAggregationType + Step int64 +} + +func (s *SelectSeriesPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + labels, err := s.GetLabelsPlanner.Process(ctx) + if err != nil { + return nil, err + } + + matchers, err := (&StreamSelectorPlanner{Selectors: s.Selectors}).getMatchers() + if err != nil { + return nil, err + } + + sampleTypeUnit := fmt.Sprintf("%s:%s", s.SampleType, s.SampleUnit) + sampleTypeUnitCond := sql.Eq(sql.NewRawObject("x.1"), sql.NewStringVal(sampleTypeUnit)) + + valueCol := sql.NewCol(sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + strSampleTypeUnit, err := sampleTypeUnitCond.String(ctx, options...) + if err != nil { + return "", err + } + return fmt.Sprintf("sum(toFloat64(arrayFirst(x -> %s, p.values_agg).2))", strSampleTypeUnit), nil + }), "value") + if s.Aggregation == v1.TimeSeriesAggregationType_TIME_SERIES_AGGREGATION_TYPE_AVERAGE { + valueCol = sql.NewCol(sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + strSampleTypeUnit, err := sampleTypeUnitCond.String(ctx, options...) + if err != nil { + return "", err + } + return fmt.Sprintf( + "sum(toFloat64(arrayFirst(x -> %s, p.values_agg).2)) / "+ + "sum(toFloat64(arrayFirst(x -> x.1 == %s).3))", + strSampleTypeUnit, strSampleTypeUnit), nil + }), "value") + } + + withLabels := sql.NewWith(labels, "labels") + var withFP *sql.With + for _, w := range labels.GetWith() { + if w.GetAlias() == "fp" { + withFP = w + break + } + } + main := sql.NewSelect(). + With(withLabels). + Select( + sql.NewSimpleCol( + fmt.Sprintf("intDiv(p.timestamp_ns, 1000000000 * %d) * %d * 1000", s.Step, s.Step), + "timestamp_ms"), + sql.NewSimpleCol("labels.new_fingerprint", "fingerprint"), + sql.NewSimpleCol("min(labels.tags)", "labels"), + valueCol). + From(sql.NewSimpleCol(ctx.ProfilesDistTable, "p")). + Join(sql.NewJoin("any left", sql.NewWithRef(withLabels), + sql.Eq(sql.NewRawObject("p.fingerprint"), sql.NewRawObject("labels.fingerprint")))). + AndWhere( + sql.NewIn(sql.NewRawObject("p.fingerprint"), sql.NewWithRef(withFP)), + sql.Ge(sql.NewRawObject("p.timestamp_ns"), sql.NewIntVal(ctx.From.UnixNano())), + sql.Le(sql.NewRawObject("p.timestamp_ns"), sql.NewIntVal(ctx.To.UnixNano()))). + GroupBy(sql.NewRawObject("timestamp_ms"), sql.NewRawObject("fingerprint")). + OrderBy(sql.NewOrderBy(sql.NewRawObject("fingerprint"), sql.ORDER_BY_DIRECTION_ASC), + sql.NewOrderBy(sql.NewRawObject("timestamp_ms"), sql.ORDER_BY_DIRECTION_ASC)) + if len(matchers.globalMatchers) > 0 { + main.AndWhere(matchers.globalMatchers...) + } + return main, nil +} diff --git a/reader/prof/transpiler/planner_select_time_series.go b/reader/prof/transpiler/planner_select_time_series.go new file mode 100644 index 00000000..aa0595b0 --- /dev/null +++ b/reader/prof/transpiler/planner_select_time_series.go @@ -0,0 +1,45 @@ +package transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/clickhouse_planner" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/prof/parser" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type TimeSeriesSelectPlanner struct { + /* StreamSelectPlanner or union of StreamSelectPlanners */ + Fp shared.SQLRequestPlanner + Selectors []parser.Selector +} + +func (t *TimeSeriesSelectPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + fp, err := t.Fp.Process(ctx) + if err != nil { + return nil, err + } + withFp := sql.NewWith(fp, "fp") + + matchers, err := (&StreamSelectorPlanner{Selectors: t.Selectors}).getMatchers() + if err != nil { + return nil, err + } + + res := sql.NewSelect(). + With(withFp). + Distinct(true). + Select( + sql.NewSimpleCol("tags", "tags"), + sql.NewSimpleCol("type_id", "type_id"), + sql.NewSimpleCol("_sample_types_units", "__sample_types_units")). + From(sql.NewSimpleCol(ctx.ProfilesSeriesDistTable, "p")). + Join(sql.NewJoin("array", sql.NewSimpleCol("sample_types_units", "_sample_types_units"), nil)). + AndWhere( + sql.NewIn(sql.NewRawObject("p.fingerprint"), sql.NewWithRef(withFp)), + sql.Ge(sql.NewRawObject("date"), sql.NewStringVal(clickhouse_planner.FormatFromDate(ctx.From))), + sql.Le(sql.NewRawObject("date"), sql.NewStringVal(clickhouse_planner.FormatFromDate(ctx.To)))) + if len(matchers.globalMatchers) > 0 { + res = res.AndWhere(matchers.globalMatchers...) + } + return res, nil +} diff --git a/reader/prof/transpiler/planner_selector.go b/reader/prof/transpiler/planner_selector.go new file mode 100644 index 00000000..7d9d23bc --- /dev/null +++ b/reader/prof/transpiler/planner_selector.go @@ -0,0 +1,171 @@ +package transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/clickhouse_planner" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/prof/parser" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type StreamSelectorPlanner struct { + Selectors []parser.Selector +} + +func (s *StreamSelectorPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + matchers, err := s.getMatchers() + if err != nil { + return nil, err + } + res := sql.NewSelect(). + Select(sql.NewRawObject("fingerprint")). + From(sql.NewRawObject(ctx.ProfilesSeriesGinTable)). + AndWhere( + sql.Ge(sql.NewRawObject("date"), sql.NewStringVal(clickhouse_planner.FormatFromDate(ctx.From))), + sql.Le(sql.NewRawObject("date"), sql.NewStringVal(clickhouse_planner.FormatFromDate(ctx.To)))). + GroupBy(sql.NewRawObject("fingerprint")) + if len(matchers.globalMatchers) > 0 { + res = res.AndWhere(sql.And(matchers.globalMatchers...)) + } + if len(matchers.kvMatchers) > 0 { + res = res. + AndWhere(sql.Or(matchers.kvMatchers...)). + AndHaving(sql.Eq( + clickhouse_planner.NewSqlBitSetAnd(matchers.kvMatchers), + sql.NewIntVal((1<<len(matchers.kvMatchers))-1))) + } + return res, nil +} + +type matchersResponse struct { + globalMatchers []sql.SQLCondition + kvMatchers []sql.SQLCondition +} + +func (s *StreamSelectorPlanner) getMatchers() (*matchersResponse, error) { + var globalClauses []sql.SQLCondition + var kvClauses []sql.SQLCondition + for _, selector := range s.Selectors { + _str, err := selector.Val.Unquote() + if err != nil { + return nil, err + } + var clause sql.SQLCondition + switch selector.Name { + case "__name__": + clause, err = s.getMatcherClause( + sql.NewRawObject("splitByChar(':', type_id)[1]"), selector.Op, sql.NewStringVal(_str)) + if err != nil { + return nil, err + } + case "__period_type__": + clause, err = s.getMatcherClause( + sql.NewRawObject("splitByChar(':', type_id)[2]"), selector.Op, sql.NewStringVal(_str)) + if err != nil { + return nil, err + } + case "__period_unit__": + clause, err = s.getMatcherClause( + sql.NewRawObject("splitByChar(':', type_id)[3]"), selector.Op, sql.NewStringVal(_str)) + if err != nil { + return nil, err + } + case "__sample_type__": + cond, err := s.getMatcherClause( + sql.NewRawObject("x.1"), + selector.Op, + sql.NewStringVal(_str)) + if err != nil { + return nil, err + } + clause = sql.Eq(s.getArrayExists(cond, sql.NewRawObject("sample_types_units")), sql.NewIntVal(1)) + case "__sample_unit__": + cond, err := s.getMatcherClause( + sql.NewRawObject("x.2"), + selector.Op, + sql.NewStringVal(_str)) + if err != nil { + return nil, err + } + clause = sql.Eq(s.getArrayExists(cond, sql.NewRawObject("sample_types_units")), sql.NewIntVal(1)) + case "__profile_type__": + fieldToMatch := "format('{}:{}:{}:{}:{}', (splitByChar(':', type_id) as _parts)[1], x.1, x.2, _parts[2], _parts[3])" + cond, err := s.getMatcherClause( + sql.NewRawObject(fieldToMatch), + selector.Op, + sql.NewStringVal(_str)) + if err != nil { + return nil, err + } + clause = sql.Eq(s.getArrayExists(cond, sql.NewRawObject("sample_types_units")), sql.NewIntVal(1)) + case "service_name": + clause, err = s.getMatcherClause(sql.NewRawObject("service_name"), selector.Op, sql.NewStringVal(_str)) + if err != nil { + return nil, err + } + } + if clause != nil { + globalClauses = append(globalClauses, clause) + continue + } + clause, err = s.getMatcherClause(sql.NewRawObject("val"), selector.Op, sql.NewStringVal(_str)) + if err != nil { + return nil, err + } + clause = sql.And(sql.Eq(sql.NewRawObject("key"), sql.NewStringVal(selector.Name)), clause) + kvClauses = append(kvClauses, clause) + } + return &matchersResponse{ + globalMatchers: globalClauses, + kvMatchers: kvClauses, + }, nil +} + +func (s *StreamSelectorPlanner) getArrayExists(cond sql.SQLCondition, field sql.SQLObject) sql.SQLObject { + return sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + strCond, err := cond.String(ctx, options...) + if err != nil { + return "", err + } + strField, err := field.String(ctx, options...) + if err != nil { + return "", err + } + return fmt.Sprintf("arrayExists(x -> %s, %s)", strCond, strField), nil + }) +} + +func (s *StreamSelectorPlanner) getMatcherClause(field sql.SQLObject, op string, + val sql.SQLObject) (sql.SQLCondition, error) { + switch op { + case "=": + return sql.Eq(field, val), nil + case "!=": + return sql.Neq(field, val), nil + case "=~": + return sql.Eq(sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + strField, err := field.String(ctx, options...) + if err != nil { + return "", err + } + strVal, err := val.String(ctx, options...) + if err != nil { + return "", err + } + return fmt.Sprintf("match(%s, %s)", strField, strVal), nil + }), sql.NewRawObject("1")), nil + case "!~": + return sql.Neq(sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + strField, err := field.String(ctx, options...) + if err != nil { + return "", err + } + strVal, err := val.String(ctx, options...) + if err != nil { + return "", err + } + return fmt.Sprintf("match(%s, %s)", strField, strVal), nil + }), sql.NewRawObject("1")), nil + } + return nil, fmt.Errorf("unknown operator: %s", op) +} diff --git a/reader/prof/transpiler/planner_union_all.go b/reader/prof/transpiler/planner_union_all.go new file mode 100644 index 00000000..3c1fded0 --- /dev/null +++ b/reader/prof/transpiler/planner_union_all.go @@ -0,0 +1,48 @@ +package transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "strings" +) + +type UnionAllPlanner struct { + Mains []shared.SQLRequestPlanner +} + +func (u *UnionAllPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + if len(u.Mains) == 0 { + return nil, fmt.Errorf("no planners provided for UNION ALL operator") + } + mains := make([]sql.ISelect, len(u.Mains)) + var err error + for i, p := range u.Mains { + mains[i], err = p.Process(ctx) + if err != nil { + return nil, err + } + } + return &unionAll{mains[0], mains[1:]}, nil +} + +type unionAll struct { + sql.ISelect + subSelects []sql.ISelect +} + +func (u *unionAll) String(ctx *sql.Ctx, options ...int) (string, error) { + strSubSelects := make([]string, len(u.subSelects)+1) + var err error + strSubSelects[0], err = u.ISelect.String(ctx, options...) + if err != nil { + return "", err + } + for i, s := range u.subSelects { + strSubSelects[i+1], err = s.String(ctx, options...) + if err != nil { + return "", err + } + } + return "(" + strings.Join(strSubSelects, ") UNION ALL (") + ")", nil +} diff --git a/reader/prof/transpiler/transpiler.go b/reader/prof/transpiler/transpiler.go new file mode 100644 index 00000000..815db6b2 --- /dev/null +++ b/reader/prof/transpiler/transpiler.go @@ -0,0 +1,131 @@ +package transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/prof/parser" + shared2 "github.com/metrico/qryn/reader/prof/shared" + v1 "github.com/metrico/qryn/reader/prof/types/v1" +) + +func PlanLabelNames(scripts []*parser.Script) (shared.SQLRequestPlanner, error) { + var fpPlanner shared.SQLRequestPlanner + if len(scripts) > 0 { + fpPlanner = &UnionAllPlanner{streamSelectorPlanners(scripts)} + } + return &LabelNamesPlanner{GenericLabelsPlanner{fpPlanner}}, nil +} + +func PlanLabelValues(scripts []*parser.Script, labelName string) (shared.SQLRequestPlanner, error) { + var fpPlanner shared.SQLRequestPlanner + if len(scripts) > 0 { + fpPlanner = &UnionAllPlanner{streamSelectorPlanners(scripts)} + } + return &LabelValuesPlanner{GenericLabelsPlanner{fpPlanner}, labelName}, nil +} + +func PlanMergeTraces(script *parser.Script, tId *shared2.TypeId) (shared.SQLRequestPlanner, error) { + _script := *script + populateTypeId(&_script, tId) + fpPlanners := streamSelectorPlanners([]*parser.Script{&_script}) + fpPlanner := fpPlanners[0] + var planner shared.SQLRequestPlanner = &MergeRawPlanner{ + Fingerprints: fpPlanner, + selectors: _script.Selectors, + sampleType: tId.SampleType, + sampleUnit: tId.SampleUnit, + } + planner = &MergeJoinedPlanner{planner} + planner = &MergeAggregatedPlanner{Main: planner} + return planner, nil +} + +func PlanSelectSeries(script *parser.Script, tId *shared2.TypeId, groupBy []string, + agg v1.TimeSeriesAggregationType, step int64) (shared.SQLRequestPlanner, error) { + _script := *script + populateTypeId(&_script, tId) + fpPlanners := streamSelectorPlanners([]*parser.Script{script}) + labelPlanner := &GetLabelsPlanner{ + FP: fpPlanners[0], + GroupBy: groupBy, + Selectors: _script.Selectors, + } + planner := &SelectSeriesPlanner{ + GetLabelsPlanner: labelPlanner, + Selectors: _script.Selectors, + SampleType: tId.SampleType, + SampleUnit: tId.SampleUnit, + Aggregation: agg, + Step: step, + } + return planner, nil +} + +func PlanMergeProfiles(script *parser.Script, tId *shared2.TypeId) (shared.SQLRequestPlanner, error) { + _script := *script + populateTypeId(&_script, tId) + fpPlanners := streamSelectorPlanners([]*parser.Script{script}) + planner := &MergeProfilesPlanner{ + Fingerprints: fpPlanners[0], + Selectors: _script.Selectors, + } + return planner, nil +} + +func PlanSeries(scripts []*parser.Script, labelNames []string) (shared.SQLRequestPlanner, error) { + selectorsCount := 0 + for _, s := range scripts { + selectorsCount += len(s.Selectors) + } + if selectorsCount == 0 { + return &AllTimeSeriesSelectPlanner{}, nil + } + fpPlanners := streamSelectorPlanners(scripts) + planners := make([]shared.SQLRequestPlanner, len(fpPlanners)) + for i, fpPlanner := range fpPlanners { + planners[i] = &TimeSeriesSelectPlanner{ + Fp: fpPlanner, + Selectors: scripts[i].Selectors, + } + } + var planner shared.SQLRequestPlanner + if len(planners) == 1 { + planner = planners[0] + } else { + planner = &UnionAllPlanner{Mains: planners} + planner = &TimeSeriesDistinctPlanner{Main: planner} + } + if len(labelNames) > 0 { + planner = &FilterLabelsPlanner{Main: planner, Labels: labelNames} + } + return planner, nil +} + +func PlanAnalyzeQuery(script *parser.Script) (shared.SQLRequestPlanner, error) { + fpPlanners := streamSelectorPlanners([]*parser.Script{script}) + var planner shared.SQLRequestPlanner = &MergeProfilesPlanner{ + Fingerprints: fpPlanners[0], + Selectors: script.Selectors, + } + planner = &ProfileSizePlanner{ + Main: planner, + } + return planner, nil +} + +func populateTypeId(script *parser.Script, tId *shared2.TypeId) { + script.Selectors = append(script.Selectors, []parser.Selector{ + {"__name__", "=", parser.Str{"`" + tId.Tp + "`"}}, + {"__period_type__", "=", parser.Str{"`" + tId.PeriodType + "`"}}, + {"__period_unit__", "=", parser.Str{"`" + tId.PeriodUnit + "`"}}, + {"__sample_type__", "=", parser.Str{"`" + tId.SampleType + "`"}}, + {"__sample_unit__", "=", parser.Str{"`" + tId.SampleUnit + "`"}}, + }...) +} + +func streamSelectorPlanners(scripts []*parser.Script) []shared.SQLRequestPlanner { + planners := make([]shared.SQLRequestPlanner, len(scripts)) + for i, script := range scripts { + planners[i] = &StreamSelectorPlanner{script.Selectors} + } + return planners +} diff --git a/reader/prof/types/v1/types.pb.go b/reader/prof/types/v1/types.pb.go new file mode 100644 index 00000000..d60b2825 --- /dev/null +++ b/reader/prof/types/v1/types.pb.go @@ -0,0 +1,1143 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.4 +// protoc v3.21.12 +// source: types/v1/types.proto + +package v1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type TimeSeriesAggregationType int32 + +const ( + TimeSeriesAggregationType_TIME_SERIES_AGGREGATION_TYPE_SUM TimeSeriesAggregationType = 0 + TimeSeriesAggregationType_TIME_SERIES_AGGREGATION_TYPE_AVERAGE TimeSeriesAggregationType = 1 +) + +// Enum value maps for TimeSeriesAggregationType. +var ( + TimeSeriesAggregationType_name = map[int32]string{ + 0: "TIME_SERIES_AGGREGATION_TYPE_SUM", + 1: "TIME_SERIES_AGGREGATION_TYPE_AVERAGE", + } + TimeSeriesAggregationType_value = map[string]int32{ + "TIME_SERIES_AGGREGATION_TYPE_SUM": 0, + "TIME_SERIES_AGGREGATION_TYPE_AVERAGE": 1, + } +) + +func (x TimeSeriesAggregationType) Enum() *TimeSeriesAggregationType { + p := new(TimeSeriesAggregationType) + *p = x + return p +} + +func (x TimeSeriesAggregationType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (TimeSeriesAggregationType) Descriptor() protoreflect.EnumDescriptor { + return file_types_v1_types_proto_enumTypes[0].Descriptor() +} + +func (TimeSeriesAggregationType) Type() protoreflect.EnumType { + return &file_types_v1_types_proto_enumTypes[0] +} + +func (x TimeSeriesAggregationType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use TimeSeriesAggregationType.Descriptor instead. +func (TimeSeriesAggregationType) EnumDescriptor() ([]byte, []int) { + return file_types_v1_types_proto_rawDescGZIP(), []int{0} +} + +type LabelPair struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LabelPair) Reset() { + *x = LabelPair{} + mi := &file_types_v1_types_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LabelPair) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LabelPair) ProtoMessage() {} + +func (x *LabelPair) ProtoReflect() protoreflect.Message { + mi := &file_types_v1_types_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LabelPair.ProtoReflect.Descriptor instead. +func (*LabelPair) Descriptor() ([]byte, []int) { + return file_types_v1_types_proto_rawDescGZIP(), []int{0} +} + +func (x *LabelPair) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *LabelPair) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type ProfileType struct { + state protoimpl.MessageState `protogen:"open.v1"` + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + SampleType string `protobuf:"bytes,4,opt,name=sample_type,json=sampleType,proto3" json:"sample_type,omitempty"` + SampleUnit string `protobuf:"bytes,5,opt,name=sample_unit,json=sampleUnit,proto3" json:"sample_unit,omitempty"` + PeriodType string `protobuf:"bytes,6,opt,name=period_type,json=periodType,proto3" json:"period_type,omitempty"` + PeriodUnit string `protobuf:"bytes,7,opt,name=period_unit,json=periodUnit,proto3" json:"period_unit,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProfileType) Reset() { + *x = ProfileType{} + mi := &file_types_v1_types_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProfileType) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProfileType) ProtoMessage() {} + +func (x *ProfileType) ProtoReflect() protoreflect.Message { + mi := &file_types_v1_types_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProfileType.ProtoReflect.Descriptor instead. +func (*ProfileType) Descriptor() ([]byte, []int) { + return file_types_v1_types_proto_rawDescGZIP(), []int{1} +} + +func (x *ProfileType) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *ProfileType) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ProfileType) GetSampleType() string { + if x != nil { + return x.SampleType + } + return "" +} + +func (x *ProfileType) GetSampleUnit() string { + if x != nil { + return x.SampleUnit + } + return "" +} + +func (x *ProfileType) GetPeriodType() string { + if x != nil { + return x.PeriodType + } + return "" +} + +func (x *ProfileType) GetPeriodUnit() string { + if x != nil { + return x.PeriodUnit + } + return "" +} + +type Labels struct { + state protoimpl.MessageState `protogen:"open.v1"` + // LabelPair is the key value pairs to identify the corresponding profile + Labels []*LabelPair `protobuf:"bytes,1,rep,name=labels,proto3" json:"labels,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Labels) Reset() { + *x = Labels{} + mi := &file_types_v1_types_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Labels) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Labels) ProtoMessage() {} + +func (x *Labels) ProtoReflect() protoreflect.Message { + mi := &file_types_v1_types_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Labels.ProtoReflect.Descriptor instead. +func (*Labels) Descriptor() ([]byte, []int) { + return file_types_v1_types_proto_rawDescGZIP(), []int{2} +} + +func (x *Labels) GetLabels() []*LabelPair { + if x != nil { + return x.Labels + } + return nil +} + +type Series struct { + state protoimpl.MessageState `protogen:"open.v1"` + Labels []*LabelPair `protobuf:"bytes,1,rep,name=labels,proto3" json:"labels,omitempty"` + Points []*Point `protobuf:"bytes,2,rep,name=points,proto3" json:"points,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Series) Reset() { + *x = Series{} + mi := &file_types_v1_types_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Series) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Series) ProtoMessage() {} + +func (x *Series) ProtoReflect() protoreflect.Message { + mi := &file_types_v1_types_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Series.ProtoReflect.Descriptor instead. +func (*Series) Descriptor() ([]byte, []int) { + return file_types_v1_types_proto_rawDescGZIP(), []int{3} +} + +func (x *Series) GetLabels() []*LabelPair { + if x != nil { + return x.Labels + } + return nil +} + +func (x *Series) GetPoints() []*Point { + if x != nil { + return x.Points + } + return nil +} + +type Point struct { + state protoimpl.MessageState `protogen:"open.v1"` + Value float64 `protobuf:"fixed64,1,opt,name=value,proto3" json:"value,omitempty"` + // Milliseconds unix timestamp + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Point) Reset() { + *x = Point{} + mi := &file_types_v1_types_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Point) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Point) ProtoMessage() {} + +func (x *Point) ProtoReflect() protoreflect.Message { + mi := &file_types_v1_types_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Point.ProtoReflect.Descriptor instead. +func (*Point) Descriptor() ([]byte, []int) { + return file_types_v1_types_proto_rawDescGZIP(), []int{4} +} + +func (x *Point) GetValue() float64 { + if x != nil { + return x.Value + } + return 0 +} + +func (x *Point) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +type LabelValuesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Matchers []string `protobuf:"bytes,2,rep,name=matchers,proto3" json:"matchers,omitempty"` + Start int64 `protobuf:"varint,3,opt,name=start,proto3" json:"start,omitempty"` + End int64 `protobuf:"varint,4,opt,name=end,proto3" json:"end,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LabelValuesRequest) Reset() { + *x = LabelValuesRequest{} + mi := &file_types_v1_types_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LabelValuesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LabelValuesRequest) ProtoMessage() {} + +func (x *LabelValuesRequest) ProtoReflect() protoreflect.Message { + mi := &file_types_v1_types_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LabelValuesRequest.ProtoReflect.Descriptor instead. +func (*LabelValuesRequest) Descriptor() ([]byte, []int) { + return file_types_v1_types_proto_rawDescGZIP(), []int{5} +} + +func (x *LabelValuesRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *LabelValuesRequest) GetMatchers() []string { + if x != nil { + return x.Matchers + } + return nil +} + +func (x *LabelValuesRequest) GetStart() int64 { + if x != nil { + return x.Start + } + return 0 +} + +func (x *LabelValuesRequest) GetEnd() int64 { + if x != nil { + return x.End + } + return 0 +} + +type LabelValuesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LabelValuesResponse) Reset() { + *x = LabelValuesResponse{} + mi := &file_types_v1_types_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LabelValuesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LabelValuesResponse) ProtoMessage() {} + +func (x *LabelValuesResponse) ProtoReflect() protoreflect.Message { + mi := &file_types_v1_types_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LabelValuesResponse.ProtoReflect.Descriptor instead. +func (*LabelValuesResponse) Descriptor() ([]byte, []int) { + return file_types_v1_types_proto_rawDescGZIP(), []int{6} +} + +func (x *LabelValuesResponse) GetNames() []string { + if x != nil { + return x.Names + } + return nil +} + +type LabelNamesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Matchers []string `protobuf:"bytes,1,rep,name=matchers,proto3" json:"matchers,omitempty"` + Start int64 `protobuf:"varint,2,opt,name=start,proto3" json:"start,omitempty"` + End int64 `protobuf:"varint,3,opt,name=end,proto3" json:"end,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LabelNamesRequest) Reset() { + *x = LabelNamesRequest{} + mi := &file_types_v1_types_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LabelNamesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LabelNamesRequest) ProtoMessage() {} + +func (x *LabelNamesRequest) ProtoReflect() protoreflect.Message { + mi := &file_types_v1_types_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LabelNamesRequest.ProtoReflect.Descriptor instead. +func (*LabelNamesRequest) Descriptor() ([]byte, []int) { + return file_types_v1_types_proto_rawDescGZIP(), []int{7} +} + +func (x *LabelNamesRequest) GetMatchers() []string { + if x != nil { + return x.Matchers + } + return nil +} + +func (x *LabelNamesRequest) GetStart() int64 { + if x != nil { + return x.Start + } + return 0 +} + +func (x *LabelNamesRequest) GetEnd() int64 { + if x != nil { + return x.End + } + return 0 +} + +type LabelNamesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LabelNamesResponse) Reset() { + *x = LabelNamesResponse{} + mi := &file_types_v1_types_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LabelNamesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LabelNamesResponse) ProtoMessage() {} + +func (x *LabelNamesResponse) ProtoReflect() protoreflect.Message { + mi := &file_types_v1_types_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LabelNamesResponse.ProtoReflect.Descriptor instead. +func (*LabelNamesResponse) Descriptor() ([]byte, []int) { + return file_types_v1_types_proto_rawDescGZIP(), []int{8} +} + +func (x *LabelNamesResponse) GetNames() []string { + if x != nil { + return x.Names + } + return nil +} + +type BlockInfo struct { + state protoimpl.MessageState `protogen:"open.v1"` + Ulid string `protobuf:"bytes,1,opt,name=ulid,proto3" json:"ulid,omitempty"` + MinTime int64 `protobuf:"varint,2,opt,name=min_time,json=minTime,proto3" json:"min_time,omitempty"` + MaxTime int64 `protobuf:"varint,3,opt,name=max_time,json=maxTime,proto3" json:"max_time,omitempty"` + Compaction *BlockCompaction `protobuf:"bytes,4,opt,name=compaction,proto3" json:"compaction,omitempty"` + Labels []*LabelPair `protobuf:"bytes,5,rep,name=labels,proto3" json:"labels,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BlockInfo) Reset() { + *x = BlockInfo{} + mi := &file_types_v1_types_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BlockInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlockInfo) ProtoMessage() {} + +func (x *BlockInfo) ProtoReflect() protoreflect.Message { + mi := &file_types_v1_types_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BlockInfo.ProtoReflect.Descriptor instead. +func (*BlockInfo) Descriptor() ([]byte, []int) { + return file_types_v1_types_proto_rawDescGZIP(), []int{9} +} + +func (x *BlockInfo) GetUlid() string { + if x != nil { + return x.Ulid + } + return "" +} + +func (x *BlockInfo) GetMinTime() int64 { + if x != nil { + return x.MinTime + } + return 0 +} + +func (x *BlockInfo) GetMaxTime() int64 { + if x != nil { + return x.MaxTime + } + return 0 +} + +func (x *BlockInfo) GetCompaction() *BlockCompaction { + if x != nil { + return x.Compaction + } + return nil +} + +func (x *BlockInfo) GetLabels() []*LabelPair { + if x != nil { + return x.Labels + } + return nil +} + +type BlockCompaction struct { + state protoimpl.MessageState `protogen:"open.v1"` + Level int32 `protobuf:"varint,1,opt,name=level,proto3" json:"level,omitempty"` + Sources []string `protobuf:"bytes,2,rep,name=sources,proto3" json:"sources,omitempty"` + Parents []string `protobuf:"bytes,3,rep,name=parents,proto3" json:"parents,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BlockCompaction) Reset() { + *x = BlockCompaction{} + mi := &file_types_v1_types_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BlockCompaction) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlockCompaction) ProtoMessage() {} + +func (x *BlockCompaction) ProtoReflect() protoreflect.Message { + mi := &file_types_v1_types_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BlockCompaction.ProtoReflect.Descriptor instead. +func (*BlockCompaction) Descriptor() ([]byte, []int) { + return file_types_v1_types_proto_rawDescGZIP(), []int{10} +} + +func (x *BlockCompaction) GetLevel() int32 { + if x != nil { + return x.Level + } + return 0 +} + +func (x *BlockCompaction) GetSources() []string { + if x != nil { + return x.Sources + } + return nil +} + +func (x *BlockCompaction) GetParents() []string { + if x != nil { + return x.Parents + } + return nil +} + +// StackTraceSelector is used for filtering stack traces by locations. +type StackTraceSelector struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Stack trace of the call site. Root at call_site[0]. + // Only stack traces having the prefix provided will be selected. + // If empty, the filter is ignored. + CallSite []*Location `protobuf:"bytes,1,rep,name=call_site,json=callSite,proto3" json:"call_site,omitempty"` + // Stack trace selector for profiles purposed for Go PGO. + // If set, call_site is ignored. + GoPgo *GoPGO `protobuf:"bytes,2,opt,name=go_pgo,json=goPgo,proto3" json:"go_pgo,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StackTraceSelector) Reset() { + *x = StackTraceSelector{} + mi := &file_types_v1_types_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StackTraceSelector) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StackTraceSelector) ProtoMessage() {} + +func (x *StackTraceSelector) ProtoReflect() protoreflect.Message { + mi := &file_types_v1_types_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StackTraceSelector.ProtoReflect.Descriptor instead. +func (*StackTraceSelector) Descriptor() ([]byte, []int) { + return file_types_v1_types_proto_rawDescGZIP(), []int{11} +} + +func (x *StackTraceSelector) GetCallSite() []*Location { + if x != nil { + return x.CallSite + } + return nil +} + +func (x *StackTraceSelector) GetGoPgo() *GoPGO { + if x != nil { + return x.GoPgo + } + return nil +} + +type Location struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Location) Reset() { + *x = Location{} + mi := &file_types_v1_types_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Location) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Location) ProtoMessage() {} + +func (x *Location) ProtoReflect() protoreflect.Message { + mi := &file_types_v1_types_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Location.ProtoReflect.Descriptor instead. +func (*Location) Descriptor() ([]byte, []int) { + return file_types_v1_types_proto_rawDescGZIP(), []int{12} +} + +func (x *Location) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type GoPGO struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Specifies the number of leaf locations to keep. + KeepLocations uint32 `protobuf:"varint,1,opt,name=keep_locations,json=keepLocations,proto3" json:"keep_locations,omitempty"` + // Aggregate callees causes the leaf location line number to be ignored, + // thus aggregating all callee samples (but not callers). + AggregateCallees bool `protobuf:"varint,2,opt,name=aggregate_callees,json=aggregateCallees,proto3" json:"aggregate_callees,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GoPGO) Reset() { + *x = GoPGO{} + mi := &file_types_v1_types_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GoPGO) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GoPGO) ProtoMessage() {} + +func (x *GoPGO) ProtoReflect() protoreflect.Message { + mi := &file_types_v1_types_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GoPGO.ProtoReflect.Descriptor instead. +func (*GoPGO) Descriptor() ([]byte, []int) { + return file_types_v1_types_proto_rawDescGZIP(), []int{13} +} + +func (x *GoPGO) GetKeepLocations() uint32 { + if x != nil { + return x.KeepLocations + } + return 0 +} + +func (x *GoPGO) GetAggregateCallees() bool { + if x != nil { + return x.AggregateCallees + } + return false +} + +type GetProfileStatsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetProfileStatsRequest) Reset() { + *x = GetProfileStatsRequest{} + mi := &file_types_v1_types_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetProfileStatsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetProfileStatsRequest) ProtoMessage() {} + +func (x *GetProfileStatsRequest) ProtoReflect() protoreflect.Message { + mi := &file_types_v1_types_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetProfileStatsRequest.ProtoReflect.Descriptor instead. +func (*GetProfileStatsRequest) Descriptor() ([]byte, []int) { + return file_types_v1_types_proto_rawDescGZIP(), []int{14} +} + +type GetProfileStatsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Whether we received any data at any time in the past. + DataIngested bool `protobuf:"varint,1,opt,name=data_ingested,json=dataIngested,proto3" json:"data_ingested,omitempty"` + // Milliseconds since epoch. + OldestProfileTime int64 `protobuf:"varint,2,opt,name=oldest_profile_time,json=oldestProfileTime,proto3" json:"oldest_profile_time,omitempty"` + // Milliseconds since epoch. + NewestProfileTime int64 `protobuf:"varint,3,opt,name=newest_profile_time,json=newestProfileTime,proto3" json:"newest_profile_time,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetProfileStatsResponse) Reset() { + *x = GetProfileStatsResponse{} + mi := &file_types_v1_types_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetProfileStatsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetProfileStatsResponse) ProtoMessage() {} + +func (x *GetProfileStatsResponse) ProtoReflect() protoreflect.Message { + mi := &file_types_v1_types_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetProfileStatsResponse.ProtoReflect.Descriptor instead. +func (*GetProfileStatsResponse) Descriptor() ([]byte, []int) { + return file_types_v1_types_proto_rawDescGZIP(), []int{15} +} + +func (x *GetProfileStatsResponse) GetDataIngested() bool { + if x != nil { + return x.DataIngested + } + return false +} + +func (x *GetProfileStatsResponse) GetOldestProfileTime() int64 { + if x != nil { + return x.OldestProfileTime + } + return 0 +} + +func (x *GetProfileStatsResponse) GetNewestProfileTime() int64 { + if x != nil { + return x.NewestProfileTime + } + return 0 +} + +var File_types_v1_types_proto protoreflect.FileDescriptor + +var file_types_v1_types_proto_rawDesc = string([]byte{ + 0x0a, 0x14, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x22, 0x35, 0x0a, 0x09, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xb5, 0x01, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x0b, + 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x1f, 0x0a, + 0x0b, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, + 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x55, 0x6e, 0x69, 0x74, 0x22, + 0x35, 0x0a, 0x06, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x2b, 0x0a, 0x06, 0x6c, 0x61, 0x62, + 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x52, 0x06, + 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x22, 0x5e, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x12, 0x2b, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, + 0x6c, 0x50, 0x61, 0x69, 0x72, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x27, 0x0a, + 0x06, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x3b, 0x0a, 0x05, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x22, 0x6c, 0x0a, 0x12, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x08, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, + 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, + 0x64, 0x22, 0x2b, 0x0a, 0x13, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x57, + 0x0a, 0x11, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x73, 0x12, + 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x2a, 0x0a, 0x12, 0x4c, 0x61, 0x62, 0x65, 0x6c, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x22, 0xbd, 0x01, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x75, 0x6c, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x69, 0x6e, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x54, 0x69, 0x6d, 0x65, + 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x52, 0x06, 0x6c, 0x61, 0x62, + 0x65, 0x6c, 0x73, 0x22, 0x5b, 0x0a, 0x0f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6d, 0x70, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x07, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, + 0x22, 0x6d, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x53, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x2f, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x73, + 0x69, 0x74, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x63, + 0x61, 0x6c, 0x6c, 0x53, 0x69, 0x74, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x67, 0x6f, 0x5f, 0x70, 0x67, + 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x6f, 0x50, 0x47, 0x4f, 0x52, 0x05, 0x67, 0x6f, 0x50, 0x67, 0x6f, 0x22, + 0x1e, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, + 0x5b, 0x0a, 0x05, 0x47, 0x6f, 0x50, 0x47, 0x4f, 0x12, 0x25, 0x0a, 0x0e, 0x6b, 0x65, 0x65, 0x70, + 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0d, 0x6b, 0x65, 0x65, 0x70, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x2b, 0x0a, 0x11, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x61, 0x6c, + 0x6c, 0x65, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x61, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x65, 0x73, 0x22, 0x18, 0x0a, 0x16, + 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9e, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x50, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x69, 0x6e, 0x67, 0x65, 0x73, + 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x61, 0x74, 0x61, 0x49, + 0x6e, 0x67, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x6f, 0x6c, 0x64, 0x65, 0x73, + 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6f, 0x6c, 0x64, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x6e, 0x65, 0x77, 0x65, 0x73, + 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6e, 0x65, 0x77, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x2a, 0x6b, 0x0a, 0x19, 0x54, 0x69, 0x6d, 0x65, 0x53, + 0x65, 0x72, 0x69, 0x65, 0x73, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x20, 0x54, 0x49, 0x4d, 0x45, 0x5f, 0x53, 0x45, 0x52, + 0x49, 0x45, 0x53, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x55, 0x4d, 0x10, 0x00, 0x12, 0x28, 0x0a, 0x24, 0x54, 0x49, + 0x4d, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, + 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x56, 0x45, 0x52, 0x41, + 0x47, 0x45, 0x10, 0x01, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x6f, 0x2f, 0x71, 0x72, 0x79, 0x6e, 0x2f, + 0x72, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x66, 0x2f, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +}) + +var ( + file_types_v1_types_proto_rawDescOnce sync.Once + file_types_v1_types_proto_rawDescData []byte +) + +func file_types_v1_types_proto_rawDescGZIP() []byte { + file_types_v1_types_proto_rawDescOnce.Do(func() { + file_types_v1_types_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_types_v1_types_proto_rawDesc), len(file_types_v1_types_proto_rawDesc))) + }) + return file_types_v1_types_proto_rawDescData +} + +var file_types_v1_types_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_types_v1_types_proto_msgTypes = make([]protoimpl.MessageInfo, 16) +var file_types_v1_types_proto_goTypes = []any{ + (TimeSeriesAggregationType)(0), // 0: types.v1.TimeSeriesAggregationType + (*LabelPair)(nil), // 1: types.v1.LabelPair + (*ProfileType)(nil), // 2: types.v1.ProfileType + (*Labels)(nil), // 3: types.v1.Labels + (*Series)(nil), // 4: types.v1.Series + (*Point)(nil), // 5: types.v1.Point + (*LabelValuesRequest)(nil), // 6: types.v1.LabelValuesRequest + (*LabelValuesResponse)(nil), // 7: types.v1.LabelValuesResponse + (*LabelNamesRequest)(nil), // 8: types.v1.LabelNamesRequest + (*LabelNamesResponse)(nil), // 9: types.v1.LabelNamesResponse + (*BlockInfo)(nil), // 10: types.v1.BlockInfo + (*BlockCompaction)(nil), // 11: types.v1.BlockCompaction + (*StackTraceSelector)(nil), // 12: types.v1.StackTraceSelector + (*Location)(nil), // 13: types.v1.Location + (*GoPGO)(nil), // 14: types.v1.GoPGO + (*GetProfileStatsRequest)(nil), // 15: types.v1.GetProfileStatsRequest + (*GetProfileStatsResponse)(nil), // 16: types.v1.GetProfileStatsResponse +} +var file_types_v1_types_proto_depIdxs = []int32{ + 1, // 0: types.v1.Labels.labels:type_name -> types.v1.LabelPair + 1, // 1: types.v1.Series.labels:type_name -> types.v1.LabelPair + 5, // 2: types.v1.Series.points:type_name -> types.v1.Point + 11, // 3: types.v1.BlockInfo.compaction:type_name -> types.v1.BlockCompaction + 1, // 4: types.v1.BlockInfo.labels:type_name -> types.v1.LabelPair + 13, // 5: types.v1.StackTraceSelector.call_site:type_name -> types.v1.Location + 14, // 6: types.v1.StackTraceSelector.go_pgo:type_name -> types.v1.GoPGO + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { file_types_v1_types_proto_init() } +func file_types_v1_types_proto_init() { + if File_types_v1_types_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_types_v1_types_proto_rawDesc), len(file_types_v1_types_proto_rawDesc)), + NumEnums: 1, + NumMessages: 16, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_types_v1_types_proto_goTypes, + DependencyIndexes: file_types_v1_types_proto_depIdxs, + EnumInfos: file_types_v1_types_proto_enumTypes, + MessageInfos: file_types_v1_types_proto_msgTypes, + }.Build() + File_types_v1_types_proto = out.File + file_types_v1_types_proto_goTypes = nil + file_types_v1_types_proto_depIdxs = nil +} diff --git a/reader/promql/parser/nodes.go b/reader/promql/parser/nodes.go new file mode 100644 index 00000000..0e58fa59 --- /dev/null +++ b/reader/promql/parser/nodes.go @@ -0,0 +1,62 @@ +package parser + +import ( + "github.com/prometheus/prometheus/model/labels" + "github.com/prometheus/prometheus/promql/parser" +) + +const ( + TPVectorSelector = 0 + TPLabelMatcher = 1 +) + +type Node interface { + GetNodeType() int +} + +type VectorSelector struct { + node *parser.VectorSelector +} + +func (v *VectorSelector) GetNodeType() int { + return TPVectorSelector +} + +func (v *VectorSelector) GetLabelMatchers() []*LabelMatcher { + res := make([]*LabelMatcher, len(v.node.LabelMatchers)) + for i, v := range v.node.LabelMatchers { + res[i] = &LabelMatcher{ + Node: v, + } + } + return res +} + +type LabelMatcher struct { + Node *labels.Matcher +} + +func (l *LabelMatcher) GetNodeType() int { + return TPLabelMatcher +} + +func (l *LabelMatcher) GetOp() string { + switch l.Node.Type { + case labels.MatchEqual: + return "=" + case labels.MatchNotEqual: + return "!=" + case labels.MatchRegexp: + return "=~" + } + //case labels.MatchNotRegexp: + return "!~" +} + +func (l *LabelMatcher) GetLabel() string { + return l.Node.Name +} + +func (l *LabelMatcher) GetVal() string { + return l.Node.Value +} diff --git a/reader/promql/parser/parser.go b/reader/promql/parser/parser.go new file mode 100644 index 00000000..56f697a2 --- /dev/null +++ b/reader/promql/parser/parser.go @@ -0,0 +1,18 @@ +package parser + +import ( + "fmt" + "github.com/prometheus/prometheus/promql/parser" +) + +func Parse(query string) (Node, error) { + expr, err := parser.ParseExpr(query) + if err != nil { + return nil, err + } + switch expr.(type) { + case *parser.VectorSelector: + return &VectorSelector{node: expr.(*parser.VectorSelector)}, nil + } + return nil, fmt.Errorf("%T not supported", expr) +} diff --git a/reader/promql/transpiler/hints_downsample_planner.go b/reader/promql/transpiler/hints_downsample_planner.go new file mode 100644 index 00000000..a372a08f --- /dev/null +++ b/reader/promql/transpiler/hints_downsample_planner.go @@ -0,0 +1,101 @@ +package transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "github.com/prometheus/prometheus/storage" +) + +type DownsampleHintsPlanner struct { + Main shared.SQLRequestPlanner + Partial bool + Hints *storage.SelectHints +} + +func (d *DownsampleHintsPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + query, err := d.Main.Process(ctx) + if err != nil { + return nil, err + } + if d.Hints.Step == 0 { + return query, nil + } + hints := d.Hints + rangeVectors := map[string]bool{ + "absent_over_time": true /*"changes": true,*/, "deriv": true, "idelta": true, "irate": true, + "rate": true, "resets": true, "min_over_time": true, "max_over_time": true, "sum_over_time": true, + "count_over_time": true, "stddev_over_time": true, "stdvar_over_time": true, "last_over_time": true, + "present_over_time": true, "delta": true, "increase": true, "avg_over_time": true, + } + + patchField(query, "value", + sql.NewSimpleCol(d.getValueMerge(hints.Func), "value").(sql.Aliased)) + if rangeVectors[hints.Func] && hints.Step > hints.Range { + timeField := fmt.Sprintf("intDiv(samples.timestamp_ns + %d000000, %d * 1000000) * %d - 1", + hints.Range, hints.Step, hints.Step) + patchField(query, "timestamp_ms", + sql.NewSimpleCol(timeField, "timestamp_ms").(sql.Aliased)) + + msInStep := sql.NewRawObject(fmt.Sprintf("timestamp_ns %% %d000000", hints.Step)) + query.AndWhere(sql.Or( + sql.Eq(msInStep, sql.NewIntVal(0)), + sql.Gt(msInStep, sql.NewIntVal(hints.Step*1000000-hints.Range*1000000)), + )) + } else { + timeField := fmt.Sprintf("intDiv(samples.timestamp_ns, %d * 1000000) * %d - 1", + hints.Step, hints.Step) + patchField(query, "timestamp_ms", + sql.NewSimpleCol(timeField, "timestamp_ms").(sql.Aliased)) + } + + return query, nil +} + +func (d *DownsampleHintsPlanner) getValueMerge(fn string) string { + supportedRangeVectors := map[string]string{ + "absent_over_time": "1", + "min_over_time": "min(min)", + "max_over_time": "max(max)", + "sum_over_time": "sum(sum)", + "count_over_time": "countMerge(count)", + "last_over_time": "argMaxMerge(samples.last)", + "present_over_time": "1", + "avg_over_time": "sum(sum) / countMerge(count)", + } + if d.Partial { + supportedRangeVectors = map[string]string{ + "absent_over_time": "1", + "min_over_time": "min(min)", + "max_over_time": "max(max)", + "sum_over_time": "sum(sum)", + "count_over_time": "countMergeState(count)", + "last_over_time": "argMaxMergeState(samples.last)", + "present_over_time": "1", + "avg_over_time": "(sum(sum), countMerge(count))", + } + } + if col, ok := supportedRangeVectors[fn]; ok { + return col + } else if d.Partial { + return "argMaxMergeState(samples.last)" + } + return "argMaxMerge(samples.last)" +} + +func (d *DownsampleHintsPlanner) getValueFinalize(fn string) string { + supportedRangeVectors := map[string]string{ + "absent_over_time": "toFloat64(1)", + "min_over_time": "min(value)", + "max_over_time": "max(value)", + "sum_over_time": "sum(value)", + "count_over_time": "countMerge(value)", + "last_over_time": "argMaxMerge(value)", + "present_over_time": "toFloat64(1)", + "avg_over_time": "sum(value.1) / sum(value.2)", + } + if col, ok := supportedRangeVectors[fn]; ok { + return col + } + return "argMaxMerge(value)" +} diff --git a/reader/promql/transpiler/init_clickhouse_planner.go b/reader/promql/transpiler/init_clickhouse_planner.go new file mode 100644 index 00000000..54275e62 --- /dev/null +++ b/reader/promql/transpiler/init_clickhouse_planner.go @@ -0,0 +1,36 @@ +package transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/clickhouse_planner" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/plugins" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type InitClickhousePlanner struct { +} + +func NewInitClickhousePlanner() shared.SQLRequestPlanner { + p := plugins.GetInitClickhousePlannerPlugin() + if p != nil { + return (*p)() + } + return &InitClickhousePlanner{} +} + +func (i *InitClickhousePlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + res := sql.NewSelect().Select( + sql.NewSimpleCol("samples.fingerprint", "fingerprint"), + sql.NewSimpleCol("samples.value", "value"), + sql.NewSimpleCol("intDiv(samples.timestamp_ns, 1000000)", "timestamp_ms"), + ).From(sql.NewSimpleCol(ctx.SamplesTableName, "samples")).AndWhere( + sql.Gt(sql.NewRawObject("samples.timestamp_ns"), sql.NewIntVal(ctx.From.UnixNano())), + sql.Le(sql.NewRawObject("samples.timestamp_ns"), sql.NewIntVal(ctx.To.UnixNano())), + clickhouse_planner.GetTypes(ctx), + ).OrderBy(sql.NewOrderBy(sql.NewRawObject("fingerprint"), sql.ORDER_BY_DIRECTION_ASC), + sql.NewOrderBy(sql.NewRawObject("samples.timestamp_ns"), sql.ORDER_BY_DIRECTION_ASC)) + if ctx.Limit > 0 { + res.Limit(sql.NewIntVal(ctx.Limit)) + } + return res, nil +} diff --git a/reader/promql/transpiler/init_downsample_clickhouse_planner.go b/reader/promql/transpiler/init_downsample_clickhouse_planner.go new file mode 100644 index 00000000..7d15a8a1 --- /dev/null +++ b/reader/promql/transpiler/init_downsample_clickhouse_planner.go @@ -0,0 +1,54 @@ +package transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/clickhouse_planner" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/plugins" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type InitDownsamplePlanner struct { + /*TODO: move to PRO !!!TURNED OFF + Use15SV2 bool + Partial bool + */ +} + +func NewInitDownsamplePlanner() shared.SQLRequestPlanner { + p := plugins.GetInitDownsamplePlannerPlugin() + if p != nil { + return (*p)() + } + return &InitDownsamplePlanner{} +} + +func (i *InitDownsamplePlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + tableName := ctx.Metrics15sTableName + /* TODO: move to PRO !!!TURNED OFF + if i.Use15SV2 { + tableName = ctx.Metrics15sV2TableName + } + */ + valueCol := "argMaxMerge(samples.last)" + /* TODO: move to PRO !!!TURNED OFF + if i.Partial { + valueCol = "argMaxMergeState(samples.last)" + }*/ + res := sql.NewSelect().Select( + sql.NewSimpleCol("samples.fingerprint", "fingerprint"), + //sql.NewSimpleCol(labelsCol, "labels"), + sql.NewSimpleCol(valueCol, "value"), + sql.NewSimpleCol("intDiv(samples.timestamp_ns, 1000000)", "timestamp_ms"), + ).From(sql.NewSimpleCol(tableName, "samples")).AndWhere( + sql.Gt(sql.NewRawObject("samples.timestamp_ns"), sql.NewIntVal(ctx.From.UnixNano())), + sql.Le(sql.NewRawObject("samples.timestamp_ns"), sql.NewIntVal(ctx.To.UnixNano())), + clickhouse_planner.GetTypes(ctx), + ).OrderBy( + sql.NewOrderBy(sql.NewRawObject("fingerprint"), sql.ORDER_BY_DIRECTION_ASC), + sql.NewOrderBy(sql.NewRawObject("timestamp_ms"), sql.ORDER_BY_DIRECTION_ASC), + ).GroupBy(sql.NewRawObject("timestamp_ms"), sql.NewRawObject("fingerprint")) + if ctx.Limit > 0 { + res.Limit(sql.NewIntVal(ctx.Limit)) + } + return res, nil +} diff --git a/reader/promql/transpiler/shared.go b/reader/promql/transpiler/shared.go new file mode 100644 index 00000000..69295f9c --- /dev/null +++ b/reader/promql/transpiler/shared.go @@ -0,0 +1,26 @@ +package transpiler + +import ( + logql_transpiler "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/clickhouse_planner" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/promql/parser" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "github.com/prometheus/prometheus/model/labels" +) + +func fingerprintsQuery(ctx *shared.PlannerContext, matchers ...*labels.Matcher) (sql.ISelect, error) { + var ( + labelNames []string + ops []string + values []string + ) + for _, _matcher := range matchers { + matcher := parser.LabelMatcher{Node: _matcher} + labelNames = append(labelNames, matcher.GetLabel()) + ops = append(ops, matcher.GetOp()) + values = append(values, matcher.GetVal()) + } + plannerStreamSelect := logql_transpiler.NewStreamSelectPlanner(labelNames, ops, values) + + return plannerStreamSelect.Process(ctx) +} diff --git a/reader/promql/transpiler/stream_select_combiner.go b/reader/promql/transpiler/stream_select_combiner.go new file mode 100644 index 00000000..32edcf25 --- /dev/null +++ b/reader/promql/transpiler/stream_select_combiner.go @@ -0,0 +1,26 @@ +package transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type StreamSelectCombiner struct { + Main shared.SQLRequestPlanner + StreamSelector shared.SQLRequestPlanner +} + +func (s *StreamSelectCombiner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := s.Main.Process(ctx) + if err != nil { + return nil, err + } + fpQuery, err := s.StreamSelector.Process(ctx) + if err != nil { + return nil, err + } + withFpQuery := sql.NewWith(fpQuery, "fp_sel") + main.AddWith(withFpQuery). + AndWhere(sql.NewIn(sql.NewRawObject("fingerprint"), sql.NewWithRef(withFpQuery))) + return main, nil +} diff --git a/reader/promql/transpiler/stream_select_planner.go b/reader/promql/transpiler/stream_select_planner.go new file mode 100644 index 00000000..d2b1d83a --- /dev/null +++ b/reader/promql/transpiler/stream_select_planner.go @@ -0,0 +1,29 @@ +package transpiler + +import ( + logql_transpiler "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/clickhouse_planner" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/promql/parser" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "github.com/prometheus/prometheus/model/labels" +) + +type StreamSelectPlanner struct { + Matchers []*labels.Matcher +} + +func (s *StreamSelectPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + var ( + labelNames []string + ops []string + values []string + ) + for _, _matcher := range s.Matchers { + matcher := parser.LabelMatcher{Node: _matcher} + labelNames = append(labelNames, matcher.GetLabel()) + ops = append(ops, matcher.GetOp()) + values = append(values, matcher.GetVal()) + } + plannerStreamSelect := logql_transpiler.NewStreamSelectPlanner(labelNames, ops, values) + return plannerStreamSelect.Process(ctx) +} diff --git a/reader/promql/transpiler/transpiler.go b/reader/promql/transpiler/transpiler.go new file mode 100644 index 00000000..14868acf --- /dev/null +++ b/reader/promql/transpiler/transpiler.go @@ -0,0 +1,132 @@ +package transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/model" + "strings" + + logql_transpiler_shared "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "github.com/prometheus/prometheus/model/labels" + "github.com/prometheus/prometheus/storage" +) + +type TranspileResponse struct { + MapResult func(samples []model.Sample) []model.Sample + Query sql.ISelect +} + +func TranspileLabelMatchers(hints *storage.SelectHints, + ctx *logql_transpiler_shared.PlannerContext, matchers ...*labels.Matcher) (*TranspileResponse, error) { + query, err := (NewInitClickhousePlanner()).Process(ctx) + if err != nil { + return nil, err + } + fpQuery, err := fingerprintsQuery(ctx, matchers...) + if err != nil { + return nil, err + } + withFingerprints := sql.NewWith(fpQuery, "fp_sel") + query = query.AddWith(withFingerprints) + query.AndWhere(sql.NewIn(sql.NewRawObject("samples.fingerprint"), sql.NewWithRef(withFingerprints))) + if hints.Step != 0 { + query = processHints(query, hints) + } + return &TranspileResponse{nil, query}, nil +} + +func processHints(query sql.ISelect, hints *storage.SelectHints) sql.ISelect { + instantVectors := map[string]bool{ + "abs": true, "absent": true, "ceil": true, "exp": true, "floor": true, + "ln": true, "log2": true, "log10": true, "round": true, "scalar": true, "sgn": true, "sort": true, "sqrt": true, + "timestamp": true, "atan": true, "cos": true, "cosh": true, "sin": true, "sinh": true, "tan": true, "tanh": true, + "deg": true, "rad": true, + } + rangeVectors := map[string]bool{ + "absent_over_time": true /*"changes": true,*/, "deriv": true, "idelta": true, "irate": true, + "rate": true, "resets": true, "min_over_time": true, "max_over_time": true, "sum_over_time": true, + "count_over_time": true, "stddev_over_time": true, "stdvar_over_time": true, "last_over_time": true, + "present_over_time": true, "delta": true, "increase": true, "avg_over_time": true, + } + if instantVectors[hints.Func] || hints.Func == "" { + withQuery := sql.NewWith(query, "spls") + query = sql.NewSelect().With(withQuery).Select( + sql.NewRawObject("fingerprint"), + //sql.NewSimpleCol("spls.labels", "labels"), + sql.NewSimpleCol("argMax(spls.value, spls.timestamp_ms)", "value"), + sql.NewSimpleCol(fmt.Sprintf("intDiv(spls.timestamp_ms - %d + %d - 1, %d) * %d + %d", + hints.Start, hints.Step, hints.Step, hints.Step, hints.Start), "timestamp_ms"), + ).From( + sql.NewWithRef(withQuery), + ).GroupBy( + sql.NewRawObject("timestamp_ms"), + sql.NewRawObject("fingerprint"), + ).OrderBy( + sql.NewOrderBy(sql.NewRawObject("fingerprint"), sql.ORDER_BY_DIRECTION_ASC), + sql.NewOrderBy(sql.NewRawObject("timestamp_ms"), sql.ORDER_BY_DIRECTION_ASC), + ) + } + if rangeVectors[hints.Func] && hints.Step > hints.Range { + msInStep := sql.NewRawObject(fmt.Sprintf("timestamp_ms %% %d", hints.Step)) + query.AndWhere(sql.Or( + sql.Eq(msInStep, sql.NewIntVal(0)), + sql.Ge(msInStep, sql.NewIntVal(hints.Step-hints.Range)), + )) + } + /*aggregators := map[string]string{ + "sum": "sum(spls.value)", + "min": "min(spls.value)", + "max": "max(spls.value)", + "group": "1", + } + if _, ok := aggregators[hints.Func]; ok && len(hints.Grouping) > 0 { + query = trimLabels(query, hints) + withPoints := sql.NewWith(query, "spls") + query = sql.NewSelect().With(withPoints).Select( + sql.NewSimpleCol("cityHash64(toString(arraySort(spls.labels)))", "fingerprint"), + sql.NewSimpleCol("spls.labels", "labels"), + sql.NewSimpleCol(aggregators[hints.Func], "value"), + sql.NewSimpleCol("spls.timestamp_ms", "timestamp_ms"), + ).From( + sql.NewWithRef(withPoints), + ).GroupBy( + sql.NewRawObject("timestamp_ms"), + sql.NewRawObject("fingerprint"), + ).OrderBy( + sql.NewOrderBy(sql.NewRawObject("fingerprint"), sql.ORDER_BY_DIRECTION_ASC), + sql.NewOrderBy(sql.NewRawObject("timestamp_ms"), sql.ORDER_BY_DIRECTION_ASC), + ) + }*/ + + return query +} + +func trimLabels(query sql.ISelect, hints *storage.SelectHints) sql.ISelect { + var labelsCol sql.SQLObject = nil + var sel []sql.SQLObject = nil + for _, col := range query.GetSelect() { + if col.(sql.Aliased).GetAlias() == "labels" { + labelsCol = col.(sql.Aliased).GetExpr() + continue + } + sel = append(sel, col) + } + if labelsCol == nil { + return query + } + patchedLabels := sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + strLabels, err := labelsCol.String(ctx, options...) + if err != nil { + return "", err + } + op := "IN" + if !hints.By { + op = "NOT IN" + } + return fmt.Sprintf("arrayFilter(x -> x.1 %s ('%s'), %s)", + op, strings.Join(hints.Grouping, `','`), strLabels), nil + }) + sel = append(sel, sql.NewCol(patchedLabels, "labels")) + query.Select(sel...) + return query +} diff --git a/reader/promql/transpiler/transpilerDownsample.go b/reader/promql/transpiler/transpilerDownsample.go new file mode 100644 index 00000000..3c80d81c --- /dev/null +++ b/reader/promql/transpiler/transpilerDownsample.go @@ -0,0 +1,134 @@ +package transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/model" + "strings" + + sql "github.com/metrico/qryn/reader/utils/sql_select" + "github.com/prometheus/prometheus/model/labels" + "github.com/prometheus/prometheus/storage" +) + +func GetLabelMatchersDownsampleRequest(hints *storage.SelectHints, + ctx *shared.PlannerContext, matchers ...*labels.Matcher) (sql.ISelect, error) { + plannerV2 := NewInitDownsamplePlanner() + selectStream := &StreamSelectPlanner{Matchers: matchers} + plannerV2 = &StreamSelectCombiner{ + Main: plannerV2, + StreamSelector: selectStream, + } + plannerV2 = &DownsampleHintsPlanner{ + Main: plannerV2, + Partial: false, + Hints: hints, + } + query, err := plannerV2.Process(ctx) + /* TODO: move to pro !!!TURNED OFF + supportV5 := ctx.VersionInfo.IsVersionSupported("v5", ctx.From.UnixNano(), ctx.To.UnixNano()) + var plannerV2 shared.SQLRequestPlanner = &InitDownsamplePlanner{ + Use15SV2: true, + Partial: !supportV5, + } + selectStream := &StreamSelectPlanner{Matchers: matchers} + plannerV2 = &StreamSelectCombiner{ + Main: plannerV2, + StreamSelector: selectStream, + } + plannerV2 = &DownsampleHintsPlanner{ + Main: plannerV2, + Partial: !supportV5, + Hints: hints, + } + + if !supportV5 { + var plannerV1 shared.SQLRequestPlanner = &InitDownsamplePlanner{ + Use15SV2: false, + Partial: true, + } + plannerV1 = &StreamSelectCombiner{ + Main: plannerV1, + StreamSelector: selectStream, + } + plannerV1 = &DownsampleHintsPlanner{ + Main: plannerV1, + Partial: true, + Hints: hints, + } + plannerV2 = &UnionPlanner{ + Main1: plannerV2, + Main2: plannerV1, + Hints: hints, + } + } + query, err := plannerV2.Process(ctx) + */ + return query, err +} + +func TranspileLabelMatchersDownsample(hints *storage.SelectHints, + ctx *shared.PlannerContext, matchers ...*labels.Matcher) (*TranspileResponse, error) { + query, err := GetLabelMatchersDownsampleRequest(hints, ctx, matchers...) + if err != nil { + return nil, err + } + if hints.Func == "count_over_time" { + return &TranspileResponse{func(samples []model.Sample) []model.Sample { + res := make([]model.Sample, 0, 10000) + for _, sample := range samples { + _samples := make([]model.Sample, int64(sample.Value)) + for i := range _samples { + _samples[i].TimestampMs = sample.TimestampMs + _samples[i].Value = 1 + } + res = append(res, _samples...) + } + return res + }, query}, nil + } + return &TranspileResponse{nil, query}, nil +} + +func trimLabelsExperimental(query sql.ISelect, hints *storage.SelectHints) sql.ISelect { + var labelsCol sql.SQLObject = nil + var sel []sql.SQLObject = nil + for _, col := range query.GetSelect() { + if col.(sql.Aliased).GetAlias() == "labels" { + labelsCol = col.(sql.Aliased).GetExpr() + continue + } + sel = append(sel, col) + } + if labelsCol == nil { + return query + } + patchedLabels := sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + strLabels, err := labelsCol.String(ctx, options...) + if err != nil { + return "", err + } + op := "IN" + if !hints.By { + op = "NOT IN" + } + return fmt.Sprintf("arrayFilter(x -> x.1 %s ('%s'), %s)", + op, strings.Join(hints.Grouping, `','`), strLabels), nil + }) + sel = append(sel, sql.NewCol(patchedLabels, "labels")) + query.Select(sel...) + return query +} + +func patchField(query sql.ISelect, alias string, newField sql.Aliased) sql.ISelect { + _select := make([]sql.SQLObject, len(query.GetSelect())) + for i, f := range query.GetSelect() { + if f.(sql.Aliased).GetAlias() != alias { + _select[i] = f + continue + } + _select[i] = newField + } + query.Select(_select...) + return query +} diff --git a/reader/promql/transpiler/union_planner.go b/reader/promql/transpiler/union_planner.go new file mode 100644 index 00000000..5d7d2c84 --- /dev/null +++ b/reader/promql/transpiler/union_planner.go @@ -0,0 +1,47 @@ +package transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/clickhouse_planner" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "github.com/prometheus/prometheus/storage" +) + +type UnionPlanner struct { + Main1 shared.SQLRequestPlanner + Main2 shared.SQLRequestPlanner + Hints *storage.SelectHints +} + +func (u *UnionPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main1, err := u.Main1.Process(ctx) + if err != nil { + return nil, err + } + main2, err := u.Main2.Process(ctx) + if err != nil { + return nil, err + } + union := &clickhouse_planner.UnionAll{ + ISelect: main1, + Anothers: []sql.ISelect{main2}, + } + + res := sql.NewSelect(). + Select( + sql.NewSimpleCol("fingerprint", "fingerprint"), + sql.NewSimpleCol((&DownsampleHintsPlanner{}).getValueFinalize(u.Hints.Func), "value"), + sql.NewSimpleCol("timestamp_ms", "timestamp_ms"), + ).From(sql.NewCol( + sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + str, err := union.String(ctx, options...) + if err != nil { + return "", err + } + return fmt.Sprintf("(%s)", str), nil + }), "samples_union")). + GroupBy(sql.NewRawObject("fingerprint"), sql.NewRawObject("timestamp_ms")). + OrderBy(sql.NewRawObject("fingerprint"), sql.NewRawObject("timestamp_ms")) + return res, nil +} diff --git a/reader/router/miscRouter.go b/reader/router/miscRouter.go new file mode 100644 index 00000000..beb1f46e --- /dev/null +++ b/reader/router/miscRouter.go @@ -0,0 +1,17 @@ +package apirouterv1 + +import ( + "github.com/gorilla/mux" + controllerv1 "github.com/metrico/qryn/reader/controller" +) + +func RouteMiscApis(app *mux.Router) { + m := &controllerv1.MiscController{ + Version: "", + } + //app.HandleFunc("/ready", m.Ready).Methods("GET") + //app.HandleFunc("/config", m.Config).Methods("GET") + app.HandleFunc("/api/v1/metadata", m.Metadata).Methods("GET") + app.HandleFunc("/api/v1/status/buildinfo", m.Buildinfo).Methods("GET") + //app.Handle("/metrics", promhttp.Handler()).Methods("GET") +} diff --git a/reader/router/profRouter.go b/reader/router/profRouter.go new file mode 100644 index 00000000..61106517 --- /dev/null +++ b/reader/router/profRouter.go @@ -0,0 +1,26 @@ +package apirouterv1 + +import ( + "github.com/gorilla/mux" + controllerv1 "github.com/metrico/qryn/reader/controller" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/prof" + "github.com/metrico/qryn/reader/service" +) + +func RouteProf(app *mux.Router, dataSession model.IDBRegistry) { + ctrl := controllerv1.ProfController{ProfService: &service.ProfService{DataSession: dataSession}} + app.HandleFunc(prof.QuerierService_ProfileTypes_FullMethodName, ctrl.ProfileTypes).Methods("POST") + app.HandleFunc(prof.QuerierService_LabelNames_FullMethodName, ctrl.LabelNames).Methods("POST") + app.HandleFunc(prof.QuerierService_LabelValues_FullMethodName, ctrl.LabelValues).Methods("POST") + app.HandleFunc(prof.QuerierService_SelectMergeStacktraces_FullMethodName, ctrl.SelectMergeStackTraces). + Methods("POST") + app.HandleFunc(prof.QuerierService_SelectSeries_FullMethodName, ctrl.SelectSeries).Methods("POST") + app.HandleFunc(prof.QuerierService_SelectMergeProfile_FullMethodName, ctrl.MergeProfiles).Methods("POST") + app.HandleFunc(prof.QuerierService_Series_FullMethodName, ctrl.Series).Methods("POST") + app.HandleFunc(prof.QuerierService_GetProfileStats_FullMethodName, ctrl.ProfileStats).Methods("POST") + app.HandleFunc(prof.SettingsService_Get_FullMethodName, ctrl.Settings).Methods("POST") + app.HandleFunc(prof.QuerierService_AnalyzeQuery_FullMethodName, ctrl.AnalyzeQuery).Methods("POST") + //app.HandleFunc("/pyroscope/render", ctrl.NotImplemented).Methods("GET") + app.HandleFunc("/pyroscope/render-diff", ctrl.RenderDiff).Methods("GET") +} diff --git a/reader/router/prometheusLblsRouter.go b/reader/router/prometheusLblsRouter.go new file mode 100644 index 00000000..5b9ea78a --- /dev/null +++ b/reader/router/prometheusLblsRouter.go @@ -0,0 +1,23 @@ +package apirouterv1 + +import ( + "github.com/gorilla/mux" + controllerv1 "github.com/metrico/qryn/reader/controller" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/service" +) + +func RouteSelectPrometheusLabels(app *mux.Router, dataSession model.IDBRegistry) { + qrService := service.NewQueryLabelsService(&model.ServiceData{ + Session: dataSession, + }) + qrCtrl := &controllerv1.PromQueryLabelsController{ + QueryLabelsService: qrService, + } + app.HandleFunc("/api/v1/labels", qrCtrl.PromLabels).Methods("GET", "POST") + app.HandleFunc("/api/v1/label/{name}/values", qrCtrl.LabelValues).Methods("GET") + app.HandleFunc("/api/v1/metadata", qrCtrl.Metadata).Methods("GET") + app.HandleFunc("/api/v1/query_exemplars", qrCtrl.Metadata).Methods("GET") + app.HandleFunc("/api/v1/rules", qrCtrl.Metadata).Methods("GET") + app.HandleFunc("/api/v1/series", qrCtrl.Series).Methods("GET", "POST") +} diff --git a/reader/router/prometheusQueryRangeRouter.go b/reader/router/prometheusQueryRangeRouter.go new file mode 100644 index 00000000..6b75b345 --- /dev/null +++ b/reader/router/prometheusQueryRangeRouter.go @@ -0,0 +1,48 @@ +package apirouterv1 + +import ( + "github.com/gorilla/mux" + "github.com/metrico/qryn/reader/config" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/service" + "time" + + kitlog "github.com/go-kit/kit/log/logrus" + grafana_re "github.com/grafana/regexp" + controllerv1 "github.com/metrico/qryn/reader/controller" + "github.com/metrico/qryn/reader/utils/logger" + "github.com/prometheus/prometheus/promql" + api_v1 "github.com/prometheus/prometheus/web/api/v1" +) + +func RoutePrometheusQueryRange(app *mux.Router, dataSession model.IDBRegistry, + stats bool) { + eng := promql.NewEngine(promql.EngineOpts{ + Logger: kitlog.NewLogrusLogger(logger.Logger), + Reg: nil, + MaxSamples: config.Cloki.Setting.SYSTEM_SETTINGS.MetricsMaxSamples, + Timeout: time.Second * 30, + ActiveQueryTracker: nil, + LookbackDelta: 0, + NoStepSubqueryIntervalFn: nil, + EnableAtModifier: false, + EnableNegativeOffset: false, + }) + svc := service.CLokiQueriable{ + ServiceData: model.ServiceData{Session: dataSession}, + } + api := api_v1.API{ + Queryable: nil, + QueryEngine: eng, + ExemplarQueryable: nil, + CORSOrigin: grafana_re.MustCompile("\\*"), + } + ctrl := &controllerv1.PromQueryRangeController{ + Controller: controllerv1.Controller{}, + Api: &api, + Storage: &svc, + Stats: stats, + } + app.HandleFunc("/api/v1/query_range", ctrl.QueryRange).Methods("GET", "POST") + app.HandleFunc("/api/v1/query", ctrl.QueryInstant).Methods("GET", "POST") +} diff --git a/reader/router/queryRangeRouter.go b/reader/router/queryRangeRouter.go new file mode 100644 index 00000000..d1dbe358 --- /dev/null +++ b/reader/router/queryRangeRouter.go @@ -0,0 +1,22 @@ +package apirouterv1 + +import ( + "github.com/gorilla/mux" + controllerv1 "github.com/metrico/qryn/reader/controller" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/service" +) + +func RouteQueryRangeApis(app *mux.Router, dataSession model.IDBRegistry) { + qrService := &service.QueryRangeService{ + ServiceData: model.ServiceData{ + Session: dataSession, + }, + } + qrCtrl := &controllerv1.QueryRangeController{ + QueryRangeService: qrService, + } + app.HandleFunc("/loki/api/v1/query_range", qrCtrl.QueryRange).Methods("GET") + app.HandleFunc("/loki/api/v1/query", qrCtrl.Query).Methods("GET") + app.HandleFunc("/loki/api/v1/tail", qrCtrl.Tail).Methods("GET") +} diff --git a/reader/router/routePluginRouter.go b/reader/router/routePluginRouter.go new file mode 100644 index 00000000..dc0ce73d --- /dev/null +++ b/reader/router/routePluginRouter.go @@ -0,0 +1,25 @@ +package apirouterv1 + +import ( + "github.com/gorilla/mux" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/plugins" + "github.com/metrico/qryn/reader/service" +) + +func PluggableRoutes(app *mux.Router, dataSession model.IDBRegistry) { + sd := model.ServiceData{ + Session: dataSession, + } + services := plugins.Services{ + TempoService: service.NewTempoService(sd), + QueryLabelsService: service.NewQueryLabelsService(&sd), + PrometheusService: &service.CLokiQueriable{ServiceData: sd}, + QueryRangeService: &service.QueryRangeService{ServiceData: sd}, + ServiceData: sd, + } + for _, r := range plugins.GetRoutePlugins() { + r.SetServices(services) + r.Route(app) + } +} diff --git a/reader/router/selectLblsRouter.go b/reader/router/selectLblsRouter.go new file mode 100644 index 00000000..a41c5aa0 --- /dev/null +++ b/reader/router/selectLblsRouter.go @@ -0,0 +1,21 @@ +package apirouterv1 + +import ( + "github.com/gorilla/mux" + controllerv1 "github.com/metrico/qryn/reader/controller" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/service" +) + +func RouteSelectLabels(app *mux.Router, dataSession model.IDBRegistry) { + qrService := service.NewQueryLabelsService(&model.ServiceData{ + Session: dataSession, + }) + qrCtrl := &controllerv1.QueryLabelsController{ + QueryLabelsService: qrService, + } + app.HandleFunc("/loki/api/v1/label", qrCtrl.Labels).Methods("GET", "POST") + app.HandleFunc("/loki/api/v1/labels", qrCtrl.Labels).Methods("GET", "POST") + app.HandleFunc("/loki/api/v1/label/{name}/values", qrCtrl.Values).Methods("GET", "POST") + app.HandleFunc("/loki/api/v1/series", qrCtrl.Series).Methods("GET", "POST") +} diff --git a/reader/router/tempoRouter.go b/reader/router/tempoRouter.go new file mode 100644 index 00000000..3cfb5488 --- /dev/null +++ b/reader/router/tempoRouter.go @@ -0,0 +1,31 @@ +package apirouterv1 + +import ( + "github.com/gorilla/mux" + controllerv1 "github.com/metrico/qryn/reader/controller" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/service" +) + +func RouteTempo(app *mux.Router, dataSession model.IDBRegistry) { + tempoSvc := service.NewTempoService(model.ServiceData{ + Session: dataSession, + }) + ctrl := &controllerv1.TempoController{ + Controller: controllerv1.Controller{}, + Service: tempoSvc, + } + app.HandleFunc("/tempo/api/traces/{traceId}", ctrl.Trace).Methods("GET") + app.HandleFunc("/api/traces/{traceId}", ctrl.Trace).Methods("GET") + app.HandleFunc("/api/traces/{traceId}/json", ctrl.Trace).Methods("GET") + app.HandleFunc("/tempo/api/echo", ctrl.Echo).Methods("GET") + app.HandleFunc("/api/echo", ctrl.Echo).Methods("GET") + app.HandleFunc("/tempo/api/search/tags", ctrl.Tags).Methods("GET") + app.HandleFunc("/api/search/tags", ctrl.Tags).Methods("GET") + app.HandleFunc("/tempo/api/search/tag/{tag}/values", ctrl.Values).Methods("GET") + app.HandleFunc("/api/search/tag/{tag}/values", ctrl.Values).Methods("GET") + app.HandleFunc("/api/v2/search/tag/{tag}/values", ctrl.ValuesV2).Methods("GET") + app.HandleFunc("/api/v2/search/tags", ctrl.TagsV2).Methods("GET") + app.HandleFunc("/tempo/api/search", ctrl.Search).Methods("GET") + app.HandleFunc("/api/search", ctrl.Search).Methods("GET") +} diff --git a/reader/service/parseOTLPJson.go b/reader/service/parseOTLPJson.go new file mode 100644 index 00000000..94756261 --- /dev/null +++ b/reader/service/parseOTLPJson.go @@ -0,0 +1,241 @@ +package service + +import ( + "encoding/base64" + "encoding/json" + "fmt" + common "go.opentelemetry.io/proto/otlp/common/v1" + v1 "go.opentelemetry.io/proto/otlp/trace/v1" + "strconv" +) + +func setTyped[T any](val any) T { + var res T + if val == nil { + return res + } + if res, ok := val.(T); ok { + return res + } + return res +} + +func setInt64(val any) int64 { + str := setTyped[string](val) + if str == "" { + return 0 + } + res, _ := strconv.ParseInt(str, 10, 64) + return res +} + +func getRawAttr(attrs []any, key string) map[string]any { + for _, attr := range attrs { + _attr := attr.(map[string]any) + if _attr["key"] == key { + return _attr + } + } + return nil +} + +func getRawVal(attrs []any, key string) map[string]any { + attr := getRawAttr(attrs, key) + if attr == nil { + return nil + } + return attr["value"].(map[string]any) +} + +func otlpGetServiceNames(attrs []any) (string, string) { + local := "" + remote := "" + for _, attr := range []string{ + "peer.service", "service.name", "faas.name", "k8s.deployment.name", "process.executable.name", + } { + val := getRawVal(attrs, attr) + if val == nil { + continue + } + _val, ok := val["stringValue"] + if !ok { + continue + } + local = _val.(string) + } + for _, attr := range []string{"service.name", "faas.name", "k8s.deployment.name", "process.executable.name"} { + val := getRawVal(attrs, attr) + if val == nil { + continue + } + _val, ok := val["stringValue"] + if !ok { + continue + } + remote = _val.(string) + } + if local == "" { + local = "OTLPResourceNoServiceName" + } + return local, remote +} + +func toFloat64(val any) float64 { + if _val, ok := val.(float64); ok { + return _val + } + if _val, ok := val.(string); ok { + res, _ := strconv.ParseFloat(_val, 64) + return res + } + return 0 +} + +func toInt64(val any) int64 { + if _val, ok := val.(int64); ok { + return _val + } + if _val, ok := val.(string); ok { + res, _ := strconv.ParseInt(_val, 10, 64) + return res + } + return 0 +} + +func setRawValue(rawVal map[string]any, val *common.AnyValue) { + if rawVal["stringValue"] != nil { + val.Value = &common.AnyValue_StringValue{ + StringValue: rawVal["stringValue"].(string), + } + } + if rawVal["intValue"] != nil { + + val.Value = &common.AnyValue_IntValue{ + IntValue: toInt64(rawVal["intValue"]), + } + } + if rawVal["boolValue"] != nil { + val.Value = &common.AnyValue_BoolValue{ + BoolValue: rawVal["boolValue"].(bool), + } + } + if rawVal["doubleValue"] != nil { + val.Value = &common.AnyValue_DoubleValue{ + DoubleValue: toFloat64(rawVal["doubleValue"]), + } + } +} + +func getAttr(attrs []*common.KeyValue, key string) *common.KeyValue { + for _, attr := range attrs { + if attr.Key == key { + return attr + } + } + return nil +} + +func setOTLPIds(rawSpan map[string]any, span *v1.Span) error { + base64DEcode := func(val any) ([]byte, error) { + if val == nil { + return nil, nil + } + if _, ok := val.(string); !ok { + return nil, fmt.Errorf("invalid traceId") + } + res, err := base64.StdEncoding.DecodeString(val.(string)) + return res, err + } + var err error + span.TraceId, err = base64DEcode(rawSpan["traceId"]) + if err != nil { + return err + } + span.SpanId, err = base64DEcode(rawSpan["spanId"]) + if err != nil { + return err + } + span.ParentSpanId, err = base64DEcode(rawSpan["parentSpanId"]) + if err != nil { + return err + } + return nil +} + +func setTimestamps(rawSpan map[string]any, span *v1.Span) { + span.StartTimeUnixNano = uint64(setInt64(rawSpan["startTimeUnixNano"])) + span.EndTimeUnixNano = uint64(setInt64(rawSpan["endTimeUnixNano"])) + events := setTyped[[]any](rawSpan["events"]) + for i, e := range events { + _e, ok := e.(map[string]any) + if !ok { + continue + } + span.Events[i].TimeUnixNano = uint64(setInt64(_e["timeUnixNano"])) + } +} + +func parseOTLPJson(payload *zipkinPayload) (*v1.Span, error) { + span := &v1.Span{} + rawSpan := make(map[string]any) + err := json.Unmarshal([]byte(payload.payload), &rawSpan) + if err != nil { + return nil, err + } + err = json.Unmarshal([]byte(payload.payload), span) + err = setOTLPIds(rawSpan, span) + if err != nil { + return nil, err + } + setTimestamps(rawSpan, span) + + attributes := setTyped[[]any](rawSpan["attributes"]) + localServiceName, remoteServiceName := otlpGetServiceNames(attributes) + attr := getAttr(span.Attributes, "service.name") + if attr != nil { + attr.Value.Value = &common.AnyValue_StringValue{ + StringValue: localServiceName, + } + } else { + span.Attributes = append(span.Attributes, &common.KeyValue{ + Key: "service.name", + Value: &common.AnyValue{ + Value: &common.AnyValue_StringValue{ + StringValue: localServiceName, + }, + }, + }) + } + attr = getAttr(span.Attributes, "remoteService.name") + if attr != nil { + attr.Value.Value = &common.AnyValue_StringValue{ + StringValue: remoteServiceName, + } + } else { + span.Attributes = append(span.Attributes, &common.KeyValue{ + Key: "remoteService.name", + Value: &common.AnyValue{ + Value: &common.AnyValue_StringValue{ + StringValue: remoteServiceName, + }, + }, + }) + } + + for _, a := range attributes { + _a, ok := a.(map[string]any) + if !ok { + continue + } + if _a["key"] == "service.name" || _a["key"] == "remoteService.name" { + continue + } + + attr := getAttr(span.Attributes, _a["key"].(string)) + if attr == nil { + continue + } + setRawValue(_a["value"].(map[string]any), attr.Value) + } + return span, err +} diff --git a/reader/service/profMerge_v1.go b/reader/service/profMerge_v1.go new file mode 100644 index 00000000..fe735039 --- /dev/null +++ b/reader/service/profMerge_v1.go @@ -0,0 +1,280 @@ +package service + +import ( + "fmt" + "github.com/metrico/qryn/reader/prof" + "github.com/metrico/qryn/writer/utils/heputils/cityhash102" + "sort" + "unsafe" +) + +func clone[T any](v *T) *T { + tmp := *v + return &tmp +} + +func sanitizeProfile(p *prof.Profile) { + if p == nil { + return + } + + ms := int64(len(p.StringTable)) + z := int64(-1) + for i, s := range p.StringTable { + if s == "" { + z = int64(i) + break + } + } + if z == -1 { + z = ms + p.StringTable = append(p.StringTable, "") + ms++ + } + + tmp := p.StringTable[0] + p.StringTable[0] = p.StringTable[z] + p.StringTable[z] = tmp + + str := func(i int64) int64 { + if i == 0 && z > 0 { + return z + } + if i == z || i >= ms || i < 0 { + return 0 + } + return i + } + + p.SampleType = removeInPlace(p.SampleType, func(x *prof.ValueType) bool { + x.Type = str(x.Type) + x.Unit = str(x.Unit) + return false + }) + + if p.PeriodType != nil { + p.PeriodType.Type = str(p.PeriodType.Type) + p.PeriodType.Unit = str(p.PeriodType.Unit) + } + + p.DefaultSampleType = str(p.DefaultSampleType) + p.DropFrames = str(p.DropFrames) + p.KeepFrames = str(p.KeepFrames) + for i := range p.Comment { + p.Comment[i] = str(p.Comment[i]) + } + + t := make(map[uint64]uint64) + j := uint64(1) + p.Mapping = removeInPlace(p.Mapping, func(x *prof.Mapping) bool { + x.BuildId = str(x.BuildId) + x.Filename = str(x.Filename) + t[x.Id] = j + x.Id = j + j++ + return false + }) + + var mapping *prof.Mapping + p.Location = removeInPlace(p.Location, func(x *prof.Location) bool { + if x.MappingId == 0 { + if mapping == nil { + mapping = &prof.Mapping{Id: uint64(len(p.Mapping)) + 1} + p.Mapping = append(p.Mapping, mapping) + } + x.MappingId = mapping.Id + return false + } + x.MappingId = t[x.MappingId] + return x.MappingId == 0 + }) + + t = make(map[uint64]uint64) + j = 1 + p.Function = removeInPlace(p.Function, func(x *prof.Function) bool { + x.Name = str(x.Name) + x.SystemName = str(x.SystemName) + x.Filename = str(x.Filename) + t[x.Id] = j + x.Id = j + j++ + return false + }) + + p.Location = removeInPlace(p.Location, func(x *prof.Location) bool { + for i := range x.Line { + line := x.Line[i] + line.FunctionId = t[line.FunctionId] + if line.FunctionId == 0 { + return true + } + } + return false + }) + + t = make(map[uint64]uint64) + j = 1 + for i := range p.Location { + x := p.Location[i] + t[x.Id] = j + x.Id = j + j++ + } + + vs := len(p.SampleType) + p.Sample = removeInPlace(p.Sample, func(x *prof.Sample) bool { + if len(x.Value) != vs { + return true + } + for i := range x.LocationId { + x.LocationId[i] = t[x.LocationId[i]] + if x.LocationId[i] == 0 { + return true + } + } + for i := range x.Label { + l := x.Label[i] + l.Key = str(l.Key) + l.Str = str(l.Str) + l.NumUnit = str(l.NumUnit) + } + return false + }) +} + +func removeInPlace[T any](slice []T, predicate func(T) bool) []T { + n := 0 + for i := range slice { + if !predicate(slice[i]) { + slice[n] = slice[i] + n++ + } + } + return slice[:n] +} + +func combineHeaders(a, b *prof.Profile) error { + err := compatible(a, b) + if err != nil { + return err + } + if a.TimeNanos == 0 || b.TimeNanos < a.TimeNanos { + a.TimeNanos = b.TimeNanos + } + a.DurationNanos += b.DurationNanos + if a.Period == 0 || a.Period < b.Period { + a.Period = b.Period + } + if a.DefaultSampleType == 0 { + a.DefaultSampleType = b.DefaultSampleType + } + return nil +} + +// You'll need to implement the compatible function as well +func compatible(a, b *prof.Profile) error { + if !equalValueType(a.PeriodType, b.PeriodType) { + return fmt.Errorf("incompatible period types %v and %v", a.PeriodType, b.PeriodType) + } + if len(b.SampleType) != len(a.SampleType) { + return fmt.Errorf("incompatible sample types %v and %v", a.SampleType, b.SampleType) + } + for i := 0; i < len(a.SampleType); i++ { + if !equalValueType(a.SampleType[i], b.SampleType[i]) { + return fmt.Errorf("incompatible sample types %v and %v", a.SampleType, b.SampleType) + } + } + return nil +} + +func equalValueType(vt1, vt2 *prof.ValueType) bool { + if vt1 == nil || vt2 == nil { + return false + } + return vt1.Type == vt2.Type && vt1.Unit == vt2.Unit +} + +func GetFunctionKey(f *prof.Function) uint64 { + str := fmt.Sprintf("%d:%d:%d:%d", f.StartLine, f.Name, f.SystemName, f.Filename) + return cityhash102.CityHash64([]byte(str), uint32(len(str))) +} + +func GetMappingKey(m *prof.Mapping) uint64 { + mapSizeRounding := uint64(0x1000) + size := m.MemoryLimit - m.MemoryStart + size = size + mapSizeRounding - 1 + size = size - (size % mapSizeRounding) + + var buildIdOrFile int64 + if m.BuildId != 0 { + buildIdOrFile = m.BuildId + } else if m.Filename != 0 { + buildIdOrFile = m.Filename + } + + str := fmt.Sprintf("%d:%d:%d", size, m.FileOffset, buildIdOrFile) + return cityhash102.CityHash64([]byte(str), uint32(len(str))) +} + +func GetLocationKey(l *prof.Location) uint64 { + lines := hashLines(l.Line) + str := fmt.Sprintf("%d:%d:%d", l.Address, lines, l.MappingId) + return cityhash102.CityHash64([]byte(str), uint32(len(str))) +} + +func hashLines(lines []*prof.Line) uint64 { + x := make([]uint64, len(lines)) + for i, line := range lines { + x[i] = line.FunctionId | (uint64(line.Line) << 32) + } + + // Convert []uint64 to []byte + u8Arr := (*[1 << 30]byte)(unsafe.Pointer(&x[0]))[:len(x)*8] + + return cityhash102.CityHash64(u8Arr, uint32(len(u8Arr))) +} + +func GetSampleKey(s *prof.Sample) uint64 { + locations := hashLocations(s.LocationId) + labels := hashProfileLabels(s.Label) + str := fmt.Sprintf("%d:%d", locations, labels) + return cityhash102.CityHash64([]byte(str), uint32(len(str))) +} + +func hashProfileLabels(labels []*prof.Label) uint64 { + if len(labels) == 0 { + return 0 + } + + // Create a copy of labels to sort + _labels := make([]*prof.Label, len(labels)) + copy(_labels, labels) + + // Sort labels + sort.Slice(_labels, func(i, j int) bool { + if _labels[i].Key < _labels[j].Key { + return true + } + if _labels[i].Key == _labels[j].Key && _labels[i].Str < _labels[j].Str { + return true + } + return false + }) + + arr := make([]uint64, len(_labels)) + for i, label := range _labels { + arr[i] = uint64(label.Key) | (uint64(label.Str) << 32) + } + + // Convert []uint64 to []byte + u8Arr := unsafe.Slice((*byte)(unsafe.Pointer(&arr[0])), len(arr)*8) + + return cityhash102.CityHash64(u8Arr, uint32(len(u8Arr))) +} + +func hashLocations(locations []uint64) uint64 { + // Convert []uint64 to []byte + u8Arr := unsafe.Slice((*byte)(unsafe.Pointer(&locations[0])), len(locations)*8) + + return cityhash102.CityHash64(u8Arr, uint32(len(u8Arr))) +} diff --git a/reader/service/profMerge_v2.go b/reader/service/profMerge_v2.go new file mode 100644 index 00000000..dc104d3a --- /dev/null +++ b/reader/service/profMerge_v2.go @@ -0,0 +1,199 @@ +package service + +import ( + "github.com/metrico/qryn/reader/prof" + "github.com/metrico/qryn/writer/utils/heputils/cityhash102" +) + +type ProfileMergeV2 struct { + prof *prof.Profile + stringTable *RewriteTableV2[string] + functionTable *RewriteTableV2[*prof.Function] + mappingTable *RewriteTableV2[*prof.Mapping] + locationTable *RewriteTableV2[*prof.Location] + sampleTable *RewriteTableV2[*prof.Sample] +} + +func NewProfileMergeV2() *ProfileMergeV2 { + return &ProfileMergeV2{ + prof: nil, + stringTable: NewRewriteTableV2[string](func(s string, i int64) string { + return s + }, hashString), + functionTable: NewRewriteTableV2[*prof.Function](func(function *prof.Function, i int64) *prof.Function { + res := clone(function) + res.Id = uint64(i) + return res + }, GetFunctionKey), + mappingTable: NewRewriteTableV2[*prof.Mapping]( + func(mapping *prof.Mapping, i int64) *prof.Mapping { + res := clone(mapping) + res.Id = uint64(i) + return res + }, GetMappingKey), + locationTable: NewRewriteTableV2[*prof.Location](func(location *prof.Location, i int64) *prof.Location { + res := clone(location) + res.Line = cloneArr(location.Line) + res.Id = uint64(i) + return res + }, GetLocationKey), + sampleTable: NewRewriteTableV2[*prof.Sample](func(sample *prof.Sample, i int64) *prof.Sample { + res := clone(sample) + res.Value = make([]int64, len(sample.Value)) + res.LocationId = append([]uint64{}, sample.LocationId...) + res.Label = cloneArr(sample.Label) + return res + }, GetSampleKey), + } +} + +func (pm *ProfileMergeV2) Merge(p *prof.Profile) error { + if len(p.Sample) == 0 || len(p.StringTable) < 2 { + return nil + } + + sanitizeProfile(p) + + strIdx := make([]int64, len(p.StringTable)) + for i := range p.StringTable { + _strIdx, _ := pm.stringTable.Get(p.StringTable[i]) + strIdx[i] = int64(_strIdx) - 1 + } + + p.PeriodType.Type = strIdx[p.PeriodType.Type] + p.PeriodType.Unit = strIdx[p.PeriodType.Unit] + for _, s := range p.SampleType { + s.Unit = strIdx[s.Unit] + s.Type = strIdx[s.Type] + } + + if pm.prof == nil { + pm.init(p) + } + + err := combineHeaders(pm.prof, p) + if err != nil { + return err + } + + fnIdx := make(map[uint64]uint64, len(p.Function)) + for _, f := range p.Function { + f.Name = strIdx[f.Name] + f.Filename = strIdx[f.Filename] + f.SystemName = strIdx[f.SystemName] + fnIdx[f.Id], _ = pm.functionTable.Get(f) + } + + mappingIdx := make(map[uint64]uint64, len(p.Mapping)) + for _, m := range p.Mapping { + m.BuildId = strIdx[m.BuildId] + m.Filename = strIdx[m.Filename] + mappingIdx[m.Id], _ = pm.mappingTable.Get(m) + } + + locationIdx := make(map[uint64]uint64, len(p.Location)) + for _, loc := range p.Location { + for _, l := range loc.Line { + l.FunctionId = fnIdx[l.FunctionId] + } + loc.MappingId = mappingIdx[loc.MappingId] + locationIdx[loc.Id], _ = pm.locationTable.Get(loc) + } + + for _, s := range p.Sample { + for _, label := range s.Label { + label.Key = strIdx[label.Key] + label.Str = strIdx[label.Str] + } + for i := range s.LocationId { + s.LocationId[i] = locationIdx[s.LocationId[i]] + } + _, _s := pm.sampleTable.Get(s) + for i := range _s.Value { + _s.Value[i] += s.Value[i] + } + } + return nil +} + +func (pm *ProfileMergeV2) init(p *prof.Profile) { + prof := &prof.Profile{ + DropFrames: p.DropFrames, + KeepFrames: p.KeepFrames, + TimeNanos: p.TimeNanos, + PeriodType: p.PeriodType, + Period: p.Period, + DefaultSampleType: p.DefaultSampleType, + } + + for _, s := range p.SampleType { + prof.SampleType = append(prof.SampleType, clone(s)) + } + pm.prof = prof +} + +func (pm *ProfileMergeV2) Profile() *prof.Profile { + if pm.prof == nil { + return &prof.Profile{} + } + p := *pm.prof + p.Sample = append([]*prof.Sample{}, pm.sampleTable.Values()...) + p.Location = append([]*prof.Location{}, pm.locationTable.Values()...) + p.Function = append([]*prof.Function{}, pm.functionTable.Values()...) + p.Mapping = append([]*prof.Mapping{}, pm.mappingTable.Values()...) + p.StringTable = append([]string{}, pm.stringTable.Values()...) + + for i := range p.Location { + p.Location[i].Id = uint64(i + 1) + } + for i := range p.Function { + p.Function[i].Id = uint64(i + 1) + } + for i := range p.Mapping { + p.Mapping[i].Id = uint64(i + 1) + } + + return &p +} + +func hashString(s string) uint64 { + return cityhash102.CityHash64([]byte(s), uint32(len(s))) +} + +func cloneArr[T any](arr []*T) []*T { + res := make([]*T, len(arr)) + for i := range arr { + res[i] = clone(arr[i]) + } + return res +} + +type RewriteTableV2[V any] struct { + Map map[uint64]uint64 + values []V + clone func(V, int64) V + index func(V) uint64 +} + +func NewRewriteTableV2[V any](clone func(V, int64) V, index func(V) uint64) *RewriteTableV2[V] { + return &RewriteTableV2[V]{ + Map: make(map[uint64]uint64), + values: make([]V, 0), + clone: clone, + index: index, + } +} + +func (rt *RewriteTableV2[V]) Get(value V) (uint64, V) { + idx := rt.index(value) + if _idx, ok := rt.Map[idx]; ok { + return _idx + 1, rt.values[_idx] + } + rt.Map[idx] = uint64(len(rt.values)) + rt.values = append(rt.values, rt.clone(value, int64(len(rt.values))+1)) + return uint64(len(rt.values)), rt.values[len(rt.values)-1] +} + +func (rt *RewriteTableV2[V]) Values() []V { + return rt.values +} diff --git a/reader/service/profService.go b/reader/service/profService.go new file mode 100644 index 00000000..52907f72 --- /dev/null +++ b/reader/service/profService.go @@ -0,0 +1,699 @@ +package service + +import ( + "context" + "fmt" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/prof" + "github.com/metrico/qryn/reader/prof/parser" + "github.com/metrico/qryn/reader/prof/shared" + v1 "github.com/metrico/qryn/reader/prof/types/v1" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "github.com/metrico/qryn/reader/utils/tables" + "google.golang.org/protobuf/proto" + "strings" + "time" +) + +var ( + TypeIDsMismatch = fmt.Errorf("left and right queries must have the same type ID") +) + +type ProfService struct { + DataSession model.IDBRegistry +} + +func (ps *ProfService) ProfileTypes(ctx context.Context, start time.Time, end time.Time) ([]*v1.ProfileType, error) { + db, err := ps.DataSession.GetDB(ctx) + if err != nil { + return nil, err + } + table := getTableName(db, tables.GetTableName("profiles_series")) + query := sql.NewSelect(). + Distinct(true). + Select( + sql.NewRawObject("type_id"), + sql.NewRawObject("sample_type_unit")). + From(sql.NewRawObject(table)). + Join(sql.NewJoin("array", sql.NewSimpleCol("sample_types_units", "sample_type_unit"), nil)). + AndWhere( + sql.Ge(sql.NewRawObject("date"), sql.NewStringVal(start.Format("2006-01-02"))), + sql.Le(sql.NewRawObject("date"), sql.NewStringVal(end.Format("2006-01-02")))) + strQ, err := query.String(sql.DefaultCtx()) + if err != nil { + return nil, err + } + result := []*v1.ProfileType{} + rows, err := db.Session.QueryCtx(ctx, strQ) + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + var typeId string + var sampleTypeUnit []any + err = rows.Scan(&typeId, &sampleTypeUnit) + if err != nil { + return nil, err + } + namePeriodTypeUnit := strings.SplitN(typeId, ":", 3) + result = append(result, &v1.ProfileType{ + ID: fmt.Sprintf("%s:%s:%s:%s:%s", + namePeriodTypeUnit[0], + sampleTypeUnit[0].(string), + sampleTypeUnit[1].(string), + namePeriodTypeUnit[1], + namePeriodTypeUnit[2]), + Name: namePeriodTypeUnit[0], + SampleType: sampleTypeUnit[0].(string), + SampleUnit: sampleTypeUnit[1].(string), + PeriodType: namePeriodTypeUnit[1], + PeriodUnit: namePeriodTypeUnit[2], + }) + } + return result, nil +} + +func (ps *ProfService) LabelNames(ctx context.Context, strScripts []string, start time.Time, + end time.Time) (*v1.LabelNamesResponse, error) { + db, err := ps.DataSession.GetDB(ctx) + if err != nil { + return nil, err + } + scripts, err := ps.parseScripts(strScripts) + if err != nil { + return nil, err + } + sel, err := prof.PlanLabelNames(ctx, scripts, start, end, db) + if err != nil { + return nil, err + } + var col string + result := &v1.LabelNamesResponse{} + err = ps.queryCols(ctx, db, sel, func() error { + result.Names = append(result.Names, col) + return nil + }, []any{&col}) + return result, nil +} + +func (ps *ProfService) LabelValues(ctx context.Context, strScripts []string, labelName string, start time.Time, + end time.Time) (*v1.LabelValuesResponse, error) { + db, err := ps.DataSession.GetDB(ctx) + if err != nil { + return nil, err + } + scripts, err := ps.parseScripts(strScripts) + if err != nil { + return nil, err + } + + sel, err := prof.PlanLabelValues(ctx, scripts, labelName, start, end, db) + if err != nil { + return nil, err + } + result := &v1.LabelValuesResponse{} + var col string + err = ps.queryCols(ctx, db, sel, func() error { + result.Names = append(result.Names, col) + return nil + }, []any{&col}) + return result, err +} + +func (ps *ProfService) MergeStackTraces(ctx context.Context, strScript string, strTypeID string, start time.Time, + end time.Time) (*prof.SelectMergeStacktracesResponse, error) { + db, err := ps.DataSession.GetDB(ctx) + if err != nil { + return nil, err + } + scripts, err := ps.parseScripts([]string{strScript}) + if err != nil { + return nil, err + } + script := scripts[0] + + typeId, err := shared.ParseTypeId(strTypeID) + if err != nil { + return nil, err + } + + tree, err := ps.getTree(ctx, script, &typeId, start, end, db) + if err != nil { + return nil, err + } + + sampleTypeUnit := fmt.Sprintf("%s:%s", typeId.SampleType, typeId.SampleUnit) + + levels := tree.BFS(sampleTypeUnit) + + res := &prof.SelectMergeStacktracesResponse{ + Flamegraph: &prof.FlameGraph{ + Names: tree.Names, + Levels: levels, + Total: tree.Total()[0], + MaxSelf: tree.MaxSelf()[0], + }, + } + return res, nil +} + +func (ps *ProfService) SelectSeries(ctx context.Context, strScript string, strTypeID string, groupBy []string, + agg v1.TimeSeriesAggregationType, step int64, start time.Time, end time.Time) (*prof.SelectSeriesResponse, error) { + db, err := ps.DataSession.GetDB(ctx) + if err != nil { + return nil, err + } + scripts, err := ps.parseScripts([]string{strScript}) + if err != nil { + return nil, err + } + script := scripts[0] + + typeId, err := shared.ParseTypeId(strTypeID) + if err != nil { + return nil, err + } + + sel, err := prof.PlanSelectSeries(ctx, script, &typeId, groupBy, agg, step, start, end, db) + if err != nil { + return nil, err + } + var ( + tsMs int64 + fp uint64 + labels [][]any + value float64 + res prof.SelectSeriesResponse + lastFp uint64 + ) + + err = ps.queryCols(ctx, db, sel, func() error { + if lastFp != fp || lastFp == 0 { + res.Series = append(res.Series, &v1.Series{ + Labels: nil, + Points: []*v1.Point{{Value: value, Timestamp: tsMs}}, + }) + lastFp = fp + for _, pair := range labels { + res.Series[len(res.Series)-1].Labels = append(res.Series[len(res.Series)-1].Labels, &v1.LabelPair{ + Name: pair[0].(string), + Value: pair[1].(string), + }) + } + return nil + } + res.Series[len(res.Series)-1].Points = append(res.Series[len(res.Series)-1].Points, &v1.Point{ + Value: value, + Timestamp: tsMs, + }) + return nil + }, []any{&tsMs, &fp, &labels, &value}) + return &res, err +} + +func (ps *ProfService) MergeProfiles(ctx context.Context, strScript string, strTypeID string, start time.Time, + end time.Time) (*prof.Profile, error) { + db, err := ps.DataSession.GetDB(ctx) + if err != nil { + return nil, err + } + scripts, err := ps.parseScripts([]string{strScript}) + if err != nil { + return nil, err + } + script := scripts[0] + + typeId, err := shared.ParseTypeId(strTypeID) + if err != nil { + return nil, err + } + + sel, err := prof.PlanMergeProfiles(ctx, script, &typeId, start, end, db) + if err != nil { + return nil, err + } + + var ( + payload []byte + merger = NewProfileMergeV2() + p prof.Profile + ) + + err = ps.queryCols(ctx, db, sel, func() error { + p.Reset() + if err != nil { + return err + } + err = proto.Unmarshal(payload, &p) + if err != nil { + return err + } + return merger.Merge(&p) + }, []any{&payload}) + if err != nil { + return nil, err + } + return merger.Profile(), nil +} + +func (ps *ProfService) TimeSeries(ctx context.Context, strScripts []string, labels []string, + start time.Time, end time.Time) (*prof.SeriesResponse, error) { + db, err := ps.DataSession.GetDB(ctx) + if err != nil { + return nil, err + } + + scripts, err := ps.parseScripts(strScripts) + if err != nil { + return nil, err + } + + sel, err := prof.PlanSeries(ctx, scripts, labels, start, end, db) + if err != nil { + return nil, err + } + + var ( + tags [][]any + typeId string + sampleTypeUnit []any + res prof.SeriesResponse + ) + + err = ps.queryCols(ctx, db, sel, func() error { + parsedTypeId, err := shared.ParseShortTypeId(typeId) + if err != nil { + return err + } + ls := &v1.Labels{} + ls.Labels = append(ls.Labels, &v1.LabelPair{Name: "__name__", Value: parsedTypeId.Tp}) + ls.Labels = append(ls.Labels, &v1.LabelPair{Name: "__period_type__", Value: parsedTypeId.PeriodType}) + ls.Labels = append(ls.Labels, &v1.LabelPair{Name: "__period_unit__", Value: parsedTypeId.PeriodUnit}) + ls.Labels = append(ls.Labels, &v1.LabelPair{Name: "__sample_type__", Value: sampleTypeUnit[0].(string)}) + ls.Labels = append(ls.Labels, &v1.LabelPair{Name: "__sample_unit__", Value: sampleTypeUnit[1].(string)}) + ls.Labels = append(ls.Labels, &v1.LabelPair{Name: "__profile_type__", Value: fmt.Sprintf( + "%s:%s:%s:%s:%s", + parsedTypeId.Tp, + sampleTypeUnit[0].(string), + sampleTypeUnit[1].(string), + parsedTypeId.PeriodType, + parsedTypeId.PeriodUnit)}) + for _, tag := range tags { + ls.Labels = append(ls.Labels, &v1.LabelPair{Name: tag[0].(string), Value: tag[1].(string)}) + } + res.LabelsSet = append(res.LabelsSet, ls) + return nil + }, []any{&tags, &typeId, &sampleTypeUnit}) + if err != nil { + return nil, err + } + return &res, nil +} + +func (ps *ProfService) ProfileStats(ctx context.Context) (*v1.GetProfileStatsResponse, error) { + db, err := ps.DataSession.GetDB(ctx) + if err != nil { + return nil, err + } + + profilesTableName := tables.GetTableName("profiles") + profilesSeriesTableName := tables.GetTableName("profiles_series") + if db.Config.ClusterName != "" { + profilesTableName = fmt.Sprintf("`%s`.%s_dist", db.Config.Name, profilesTableName) + profilesSeriesTableName = fmt.Sprintf("`%s`.%s_dist", db.Config.Name, profilesSeriesTableName) + } + + brackets := func(object sql.SQLObject) sql.SQLObject { + return sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + strObject, err := object.String(ctx, options...) + if err != nil { + return "", err + } + return fmt.Sprintf("(%s)", strObject), nil + }) + } + + dateToNS := func(object sql.SQLObject) sql.SQLObject { + return sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + strObject, err := object.String(ctx, options...) + if err != nil { + return "", err + } + return fmt.Sprintf("toUnixTimestamp((%s)) * 1000000000", strObject), nil + }) + } + + nonEmptyReq := sql.NewSelect(). + Select(sql.NewSimpleCol("any(1::Int8)", "non_empty")). + From(sql.NewRawObject(profilesTableName)) + withNonEmpty := sql.NewWith(nonEmptyReq, "non_empty") + + minDateReq := sql.NewSelect(). + Select(sql.NewSimpleCol("min(date)", "min_date"), + sql.NewSimpleCol("max(date)", "max_date")). + From(sql.NewRawObject(profilesSeriesTableName)) + withMinDate := sql.NewWith(minDateReq, "min_date") + + minTimeReq := sql.NewSelect(). + Select( + sql.NewSimpleCol("intDiv(min(timestamp_ns), 1000000)", "min_time"), + sql.NewSimpleCol("intDiv(max(timestamp_ns), 1000000)", "max_time")). + From(sql.NewRawObject(profilesTableName)). + OrWhere( + sql.Lt( + sql.NewRawObject("timestamp_ns"), + dateToNS(sql.NewSelect().Select(sql.NewRawObject("any(min_date + INTERVAL '1 day')")). + From(sql.NewWithRef(withMinDate)))), + sql.Ge( + sql.NewRawObject("timestamp_ns"), + dateToNS(sql.NewSelect().Select(sql.NewRawObject("any(max_date)")). + From(sql.NewWithRef(withMinDate)))), + ) + withMinTime := sql.NewWith(minTimeReq, "min_time") + + req := sql.NewSelect(). + With(withNonEmpty, withMinDate, withMinTime). + Select( + sql.NewCol( + brackets(sql.NewSelect().Select(sql.NewRawObject("any(non_empty)")).From(sql.NewWithRef(withNonEmpty))), + "non_empty"), + sql.NewCol( + brackets(sql.NewSelect().Select(sql.NewRawObject("any(min_time)")).From(sql.NewWithRef(withMinTime))), + "min_date"), + sql.NewCol( + brackets(sql.NewSelect().Select(sql.NewRawObject("any(max_time)")).From(sql.NewWithRef(withMinTime))), + "min_time")) + + var ( + nonEmpty int8 + minTime int64 + maxTime int64 + res v1.GetProfileStatsResponse + ) + + err = ps.queryCols(ctx, db, req, func() error { + res.DataIngested = nonEmpty != 0 + res.OldestProfileTime = minTime + res.NewestProfileTime = maxTime + return nil + }, []any{&nonEmpty, &minTime, &maxTime}) + if err != nil { + return nil, err + } + return &res, nil +} + +func (ps *ProfService) Settings(ctx context.Context) (*prof.GetSettingsResponse, error) { + return &prof.GetSettingsResponse{ + Settings: []*prof.Setting{ + { + Name: "pluginSettings", + Value: "{}", + ModifiedAt: time.Now().UnixMilli(), + }, + }, + }, nil +} + +func (ps *ProfService) RenderDiff(ctx context.Context, + strLeftQuery string, strRightQuery string, + leftFrom time.Time, rightFrom time.Time, + leftTo time.Time, rightTo time.Time) (*Flamebearer, error) { + db, err := ps.DataSession.GetDB(ctx) + if err != nil { + return nil, err + } + + strLeftTypeId, strLeftScript, err := ps.detachTypeId(strLeftQuery) + if err != nil { + return nil, err + } + + strRightTypeId, strRightScript, err := ps.detachTypeId(strRightQuery) + if err != nil { + return nil, err + } + + if strLeftTypeId != strRightTypeId { + return nil, TypeIDsMismatch + } + + scripts, err := ps.parseScripts([]string{strLeftScript, strRightScript}) + if err != nil { + return nil, err + } + + leftTypeId, err := shared.ParseTypeId(strLeftTypeId) + if err != nil { + return nil, err + } + + rightTypeId, err := shared.ParseTypeId(strRightTypeId) + if err != nil { + return nil, err + } + + leftTree, err := ps.getTree(ctx, scripts[0], &leftTypeId, leftFrom, leftTo, db) + if err != nil { + return nil, err + } + + rightTree, err := ps.getTree(ctx, scripts[1], &rightTypeId, rightFrom, rightTo, db) + if err != nil { + return nil, err + } + + if !assertPositive(leftTree) { + return nil, fmt.Errorf("left tree is not positive") + } + + if !assertPositive(rightTree) { + return nil, fmt.Errorf("right tree is not positive") + } + + synchronizeNames(leftTree, rightTree) + mergeNodes(leftTree, rightTree) + diff := computeFlameGraphDiff(leftTree, rightTree) + fb := ps.diffToFlameBearer(diff, &leftTypeId) + return fb, nil +} + +func (ps *ProfService) AnalyzeQuery(ctx context.Context, strQuery string, + from time.Time, to time.Time) (*prof.AnalyzeQueryResponse, error) { + db, err := ps.DataSession.GetDB(ctx) + if err != nil { + return nil, err + } + + query, err := parser.Parse(strQuery) + if err != nil { + return nil, err + } + + req, err := prof.PlanAnalyzeQuery(ctx, query, from, to, db) + if err != nil { + return nil, err + } + + var ( + size int64 + fps int64 + ) + + err = ps.queryCols(ctx, db, req, func() error { return nil }, []any{&size, &fps}) + if err != nil { + return nil, err + } + + return &prof.AnalyzeQueryResponse{ + QueryScopes: []*prof.QueryScope{ + {ComponentType: "store", ComponentCount: 1}, + }, + QueryImpact: &prof.QueryImpact{ + TotalBytesInTimeRange: uint64(size), + TotalQueriedSeries: uint64(fps), + }, + }, nil +} + +func (ps *ProfService) getTree(ctx context.Context, script *parser.Script, typeId *shared.TypeId, + start, end time.Time, db *model.DataDatabasesMap) (*Tree, error) { + sel, err := prof.PlanMergeTraces(ctx, script, typeId, start, end, db) + if err != nil { + return nil, err + } + + var ( + treeNodes [][]any + functions [][]any + ) + + err = ps.queryCols(ctx, db, sel, func() error { return nil }, []any{&treeNodes, &functions}) + if err != nil { + return nil, err + } + + sampleTypeUnit := fmt.Sprintf("%s:%s", typeId.SampleType, typeId.SampleUnit) + tree := NewTree() + tree.SampleTypes = []string{sampleTypeUnit} + tree.MergeTrie(treeNodes, functions, sampleTypeUnit) + return tree, nil +} + +func (ps *ProfService) parseScripts(strScripts []string) ([]*parser.Script, error) { + var err error + scripts := make([]*parser.Script, len(strScripts)) + for i, strScript := range strScripts { + scripts[i], err = parser.Parse(strScript) + if err != nil { + return nil, err + } + } + return scripts, err +} + +func (ps *ProfService) queryCols(ctx context.Context, db *model.DataDatabasesMap, sel sql.ISelect, + f func() error, col []any) error { + strSel, err := sel.String(sql.DefaultCtx()) + if err != nil { + return err + } + fmt.Println(strSel) + rows, err := db.Session.QueryCtx(ctx, strSel) + if err != nil { + return err + } + defer rows.Close() + + for rows.Next() { + err = rows.Scan(col...) + if err != nil { + return err + } + err = f() + if err != nil { + return err + } + } + return nil +} + +func (ps *ProfService) detachTypeId(strQuery string) (string, string, error) { + typeAndQuery := strings.SplitN(strQuery, "{", 2) + if len(typeAndQuery) != 2 { + return "", "", fmt.Errorf("invalid query format: %s", strQuery) + } + typeId := strings.TrimSpace(typeAndQuery[0]) + query := "{" + strings.TrimSpace(typeAndQuery[1]) + return typeId, query, nil +} + +type units = string +type Flamebearer struct { + Version int `json:"version"` + FlamebearerProfileV1 FlamebearerProfileV1 `json:"flamebearerProfileV1"` + Telemetry map[string]interface{} `json:"telemetry,omitempty"` +} + +type FlamebearerProfileV1 struct { + Flamebearer *FlamebearerV1 `json:"flamebearer"` + Metadata FlamebearerMetadataV1 `json:"metadata"` + Timeline *FlamebearerTimelineV1 `json:"timeline"` + Groups map[string]FlamebearerTimelineV1 `json:"groups"` + Heatmap *Heatmap `json:"heatmap"` + LeftTicks int64 `json:"leftTicks"` + RightTicks int64 `json:"rightTicks"` +} + +type FlamebearerV1 struct { + Names []string `json:"names"` + Levels [][]int64 `json:"levels"` + NumTicks int `json:"numTicks"` + MaxSelf int `json:"maxSelf"` +} + +type FlamebearerMetadataV1 struct { + Format string `json:"format"` + SpyName string `json:"spyName"` + SampleRate int64 `json:"sampleRate"` + Units units `json:"units"` + Name string `json:"name"` +} + +type FlamebearerTimelineV1 struct { + StartTime int64 `json:"startTime"` + Samples []uint64 `json:"samples"` + DurationDelta int64 `json:"durationDelta"` + Watermarks map[int]int64 `json:"watermarks"` +} + +type Heatmap struct { + Values [][]uint64 `json:"values"` + TimeBuckets int64 `json:"timeBuckets"` + ValueBuckets int64 `json:"valueBuckets"` + StartTime int64 `json:"startTime"` + EndTime int64 `json:"endTime"` + MinValue uint64 `json:"minValue"` + MaxValue uint64 `json:"maxValue"` + MinDepth uint64 `json:"minDepth"` + MaxDepth uint64 `json:"maxDepth"` +} + +func (ps *ProfService) diffToFlameBearer(diff *prof.FlameGraphDiff, typeId *shared.TypeId) *Flamebearer { + flameGraph := &prof.FlameGraph{ + Names: diff.Names, + Levels: diff.Levels, + Total: diff.Total, + MaxSelf: diff.MaxSelf, + } + flameBearer := ps.flameGraphToFlameBearer(flameGraph, typeId) + flameBearer.FlamebearerProfileV1.LeftTicks = diff.LeftTicks + flameBearer.FlamebearerProfileV1.RightTicks = diff.RightTicks + flameBearer.FlamebearerProfileV1.Metadata.Format = "double" + return flameBearer +} + +func (ps *ProfService) flameGraphToFlameBearer(flameGraph *prof.FlameGraph, typeId *shared.TypeId) *Flamebearer { + if flameGraph == nil { + flameGraph = &prof.FlameGraph{} + } + unit := typeId.SampleUnit + sampleRate := 100 + switch typeId.SampleType { + case "inuse_objects", "alloc_objects", "goroutine", "samples": + unit = "objects" + case "cpu": + unit = "samples" + sampleRate = 1000000000 + } + + flameBearer := &FlamebearerV1{ + Names: flameGraph.Names, + NumTicks: int(flameGraph.GetTotal()), + MaxSelf: int(flameGraph.GetMaxSelf()), + } + for _, l := range flameGraph.Levels { + level := make([]int64, len(l.Values)) + for i, v := range l.Values { + level[i] = v + } + flameBearer.Levels = append(flameBearer.Levels, level) + } + + metadata := &FlamebearerMetadataV1{ + Format: "single", + SampleRate: int64(sampleRate), + Units: unit, + Name: typeId.SampleType, + } + + return &Flamebearer{ + Version: 1, + FlamebearerProfileV1: FlamebearerProfileV1{ + Flamebearer: flameBearer, + Metadata: *metadata, + }, + } +} diff --git a/reader/service/profTree.go b/reader/service/profTree.go new file mode 100644 index 00000000..b008e658 --- /dev/null +++ b/reader/service/profTree.go @@ -0,0 +1,458 @@ +package service + +import ( + "github.com/metrico/qryn/reader/prof" + "sort" +) + +type Tree struct { + Names []string + NamesMap map[uint64]int + Nodes map[uint64][]*TreeNodeV2 + SampleTypes []string + maxSelf []int64 + NodesNum int32 + Pprof *prof.Profile +} + +func NewTree() *Tree { + return &Tree{ + Nodes: make(map[uint64][]*TreeNodeV2), + NamesMap: map[uint64]int{}, + Names: []string{"total", "n/a"}, + maxSelf: []int64{0}, + } +} + +func (t *Tree) MaxSelf() []int64 { + if len(t.maxSelf) == 0 { + return []int64{0} + } + return t.maxSelf +} + +func (t *Tree) Total() []int64 { + if children, ok := t.Nodes[0]; ok && len(children) > 0 { + total := make([]int64, len(children[0].Total)) + for _, child := range children { + for i, childTotal := range child.Total { + total[i] += childTotal + } + } + return total + } + return []int64{0} +} + +func (t *Tree) AddName(name string, nameHash uint64) { + if _, exists := t.NamesMap[nameHash]; !exists { + t.Names = append(t.Names, name) + t.NamesMap[nameHash] = len(t.Names) - 1 + } +} + +type TreeNodeV2 struct { + FnID uint64 + NodeID uint64 + Self []int64 + Total []int64 +} + +func (n *TreeNodeV2) Clone() *TreeNodeV2 { + return &TreeNodeV2{ + FnID: n.FnID, + NodeID: n.NodeID, + Self: append([]int64(nil), n.Self...), + Total: append([]int64(nil), n.Total...), + } +} + +func (n *TreeNodeV2) SetTotalAndSelf(self []int64, total []int64) *TreeNodeV2 { + res := n.Clone() + res.Self = self + res.Total = total + return res +} + +func (t *Tree) MergeTrie(nodes [][]any, functions [][]any, sampleType string) { + sampleTypeIndex := -1 + for i, st := range t.SampleTypes { + if st == sampleType { + sampleTypeIndex = i + break + } + } + if sampleTypeIndex == -1 { + return + } + + for _, f := range functions { + id := f[0].(uint64) + fn := f[1].(string) + if len(t.NamesMap) < 2_000_000 { + if _, exists := t.NamesMap[id]; !exists { + t.Names = append(t.Names, fn) + t.NamesMap[id] = len(t.Names) - 1 + } + } + } + + for _, _n := range nodes { + parentID := _n[0].(uint64) + fnID := _n[1].(uint64) + nodeID := _n[2].(uint64) + selfValue := _n[3].(int64) + totalValue := _n[4].(int64) + + if t.maxSelf[sampleTypeIndex] < selfValue { + t.maxSelf[sampleTypeIndex] = selfValue + } + + slf := make([]int64, len(t.SampleTypes)) + slf[sampleTypeIndex] = selfValue + + total := make([]int64, len(t.SampleTypes)) + total[sampleTypeIndex] = totalValue + + if children, ok := t.Nodes[parentID]; ok { + if pos := findNode(nodeID, children); pos != -1 { + node := children[pos].Clone() + node.Self[sampleTypeIndex] += selfValue + node.Total[sampleTypeIndex] += totalValue + children[pos] = node + continue + } + } + + if t.NodesNum >= 2_000_000 { + return + } + + t.Nodes[parentID] = append(t.Nodes[parentID], &TreeNodeV2{ + FnID: fnID, + NodeID: nodeID, + Self: slf, + Total: total, + }) + + t.NodesNum++ + } +} + +func (t *Tree) BFS(sampleType string) []*prof.Level { + sampleTypeIndex := -1 + for i, st := range t.SampleTypes { + if st == sampleType { + sampleTypeIndex = i + break + } + } + if sampleTypeIndex == -1 { + return nil + } + + res := make([]*prof.Level, 0) + rootChildren := t.Nodes[0] + + var total int64 + for _, child := range rootChildren { + total += child.Total[sampleTypeIndex] + } + + res = append(res, &prof.Level{Values: []int64{0, total, 0, 0}}) + + totals := make([]int64, len(t.SampleTypes)) + totals[sampleTypeIndex] = total + + totalNode := &TreeNodeV2{ + Self: make([]int64, len(t.SampleTypes)), + Total: totals, + NodeID: 0, + FnID: 0, + } + + prependMap := make(map[uint64]int64) + reviewed := make(map[uint64]bool) + + currentLevelNodes := []*TreeNodeV2{totalNode} + + for len(currentLevelNodes) > 0 { + var nextLevelNodes []*TreeNodeV2 + var prepend int64 + lvl := prof.Level{} + + for _, parent := range currentLevelNodes { + prepend += prependMap[parent.NodeID] + children, ok := t.Nodes[parent.NodeID] + if !ok { + prepend += parent.Total[sampleTypeIndex] + continue + } + for _, child := range children { + if reviewed[child.NodeID] { + return res + } + reviewed[child.NodeID] = true + + prependMap[child.NodeID] = prepend + nextLevelNodes = append(nextLevelNodes, child) + + lvl.Values = append(lvl.Values, + prepend, + child.Total[sampleTypeIndex], + child.Self[sampleTypeIndex], + int64(t.NamesMap[child.FnID]), + ) + + prepend = 0 + } + + prepend += parent.Self[sampleTypeIndex] + } + + res = append(res, &lvl) + currentLevelNodes = nextLevelNodes + } + + return res +} + +func synchronizeNames(t1, t2 *Tree) { + // Synchronize names from t1 to t2 + namesToAddToT2 := make([]struct { + id uint64 + name string + }, 0) + + for id, idx := range t1.NamesMap { + if _, exists := t2.NamesMap[id]; !exists { + namesToAddToT2 = append(namesToAddToT2, struct { + id uint64 + name string + }{id, t1.Names[idx]}) + } + } + + for _, nameData := range namesToAddToT2 { + t2.AddName(nameData.name, nameData.id) + } + + // Synchronize names from t2 to t1 + namesToAddToT1 := make([]struct { + id uint64 + name string + }, 0) + + for id, idx := range t2.NamesMap { + if _, exists := t1.NamesMap[id]; !exists { + namesToAddToT1 = append(namesToAddToT1, struct { + id uint64 + name string + }{id, t2.Names[idx]}) + } + } + + for _, nameData := range namesToAddToT1 { + t1.AddName(nameData.name, nameData.id) + } +} + +func assertPositive(t *Tree) bool { + for _, nodes := range t.Nodes { + for _, node := range nodes { + for _, selfValue := range node.Self { + if selfValue < 0 { + return false + } + } + } + } + return true +} + +func mergeNodes(t1, t2 *Tree) { + keys := make(map[uint64]struct{}) + for k := range t1.Nodes { + keys[k] = struct{}{} + } + for k := range t2.Nodes { + keys[k] = struct{}{} + } + + for key := range keys { + t1Children, ok1 := t1.Nodes[key] + if !ok1 { + t1Children = []*TreeNodeV2{} + } + t2Children, ok2 := t2.Nodes[key] + if !ok2 { + t2Children = []*TreeNodeV2{} + } + + sort.Slice(t1Children, func(i, j int) bool { + return t1Children[i].NodeID < t1Children[j].NodeID + }) + sort.Slice(t2Children, func(i, j int) bool { + return t2Children[i].NodeID < t2Children[j].NodeID + }) + + newT1Nodes, newT2Nodes := mergeChildren(t1Children, t2Children) + t1.Nodes[key] = newT1Nodes + t2.Nodes[key] = newT2Nodes + } +} + +func computeFlameGraphDiff(t1, t2 *Tree) *prof.FlameGraphDiff { + res := &prof.FlameGraphDiff{} + res.LeftTicks = t1.Total()[0] + res.RightTicks = t2.Total()[0] + res.Total = res.LeftTicks + res.RightTicks + + leftNodes := []*TreeNodeV2{{ + FnID: 0, + NodeID: 0, + Self: []int64{0}, + Total: []int64{res.LeftTicks}, + }} + + rightNodes := []*TreeNodeV2{{ + FnID: 0, + NodeID: 0, + Self: []int64{0}, + Total: []int64{res.RightTicks}, + }} + + levels := []int{0} + xLeftOffsets := []int64{0} + xRightOffsets := []int64{0} + nameLocationCache := make(map[string]int64) + + for len(leftNodes) > 0 && len(rightNodes) > 0 { + left := leftNodes[0] + right := rightNodes[0] + leftNodes = leftNodes[1:] + rightNodes = rightNodes[1:] + + xLeftOffset := xLeftOffsets[0] + xRightOffset := xRightOffsets[0] + xLeftOffsets = xLeftOffsets[1:] + xRightOffsets = xRightOffsets[1:] + + level := levels[0] + levels = levels[1:] + + var name string + if left.FnID == 0 { + name = "total" + } else { + name = t1.Names[t1.NamesMap[left.FnID]] + } + + nameIdx, ok := nameLocationCache[name] + if !ok { + nameIdx = int64(len(res.Names)) + res.Names = append(res.Names, name) + nameLocationCache[name] = nameIdx + } + + for len(res.Levels) <= level { + res.Levels = append(res.Levels, &prof.Level{}) + } + + if res.MaxSelf < left.Self[0] { + res.MaxSelf = left.Self[0] + } + if res.MaxSelf < right.Self[0] { + res.MaxSelf = right.Self[0] + } + + res.Levels[level].Values = append(res.Levels[level].Values, + xLeftOffset, left.Total[0], left.Self[0], + xRightOffset, right.Total[0], right.Self[0], + nameIdx) + + if childrenLeft, ok := t1.Nodes[left.NodeID]; ok { + childrenRight, _ := t2.Nodes[right.NodeID] + for i := len(childrenLeft) - 1; i >= 0; i-- { + childLeft := childrenLeft[i] + var childRight *TreeNodeV2 + if i < len(childrenRight) { + childRight = childrenRight[i] + } else { + childRight = &TreeNodeV2{Self: []int64{0}, Total: []int64{0}} + } + leftNodes = append(leftNodes, childLeft) + rightNodes = append(rightNodes, childRight) + xLeftOffsets = append(xLeftOffsets, xLeftOffset) + xRightOffsets = append(xRightOffsets, xRightOffset) + xLeftOffset += childLeft.Total[0] + xRightOffset += childRight.Total[0] + levels = append(levels, level+1) + } + } + } + + for i := range res.Levels { + var prev0, prev3 int64 + for j := 0; j < len(res.Levels[i].Values); j += 7 { + res.Levels[i].Values[j] -= prev0 + prev0 += res.Levels[i].Values[j] + res.Levels[i].Values[j+1] + res.Levels[i].Values[j+3] -= prev3 + prev3 += res.Levels[i].Values[j+3] + res.Levels[i].Values[j+4] + } + } + + return res +} + +func mergeChildren(t1Nodes, t2Nodes []*TreeNodeV2) ([]*TreeNodeV2, []*TreeNodeV2) { + var newT1Nodes, newT2Nodes []*TreeNodeV2 + i, j := 0, 0 + + for i < len(t1Nodes) && j < len(t2Nodes) { + if t1Nodes[i].NodeID == t2Nodes[j].NodeID { + newT1Nodes = append(newT1Nodes, t1Nodes[i].Clone()) + newT2Nodes = append(newT2Nodes, t2Nodes[j].Clone()) + i++ + j++ + } else if t1Nodes[i].NodeID < t2Nodes[j].NodeID { + newT1Nodes = append(newT1Nodes, t1Nodes[i].Clone()) + newT2Nodes = append(newT2Nodes, createEmptyNode(t1Nodes[i])) + i++ + } else { + newT2Nodes = append(newT2Nodes, t2Nodes[j].Clone()) + newT1Nodes = append(newT1Nodes, createEmptyNode(t2Nodes[j])) + j++ + } + } + + for ; i < len(t1Nodes); i++ { + newT1Nodes = append(newT1Nodes, t1Nodes[i].Clone()) + newT2Nodes = append(newT2Nodes, createEmptyNode(t1Nodes[i])) + } + + for ; j < len(t2Nodes); j++ { + newT2Nodes = append(newT2Nodes, t2Nodes[j].Clone()) + newT1Nodes = append(newT1Nodes, createEmptyNode(t2Nodes[j])) + } + + return newT1Nodes, newT2Nodes +} + +func createEmptyNode(node *TreeNodeV2) *TreeNodeV2 { + return &TreeNodeV2{ + NodeID: node.NodeID, + FnID: node.FnID, + Self: make([]int64, len(node.Self)), + Total: make([]int64, len(node.Total)), + } +} + +func findNode(nodeID uint64, children []*TreeNodeV2) int { + for i, child := range children { + if child.NodeID == nodeID { + return i + } + } + return -1 +} diff --git a/reader/service/promQueryable.go b/reader/service/promQueryable.go new file mode 100644 index 00000000..208c590a --- /dev/null +++ b/reader/service/promQueryable.go @@ -0,0 +1,397 @@ +package service + +import ( + "bytes" + "context" + "fmt" + "github.com/VictoriaMetrics/fastcache" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/plugins" + "github.com/metrico/qryn/reader/utils/cityhash102" + "github.com/metrico/qryn/reader/utils/dbVersion" + "github.com/metrico/qryn/reader/utils/logger" + "github.com/metrico/qryn/reader/utils/tables" + "math/rand" + "sort" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/metrico/qryn/reader/promql/transpiler" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "github.com/prometheus/prometheus/model/labels" + "github.com/prometheus/prometheus/storage" +) + +var cache = fastcache.New(100 * 1024 * 1024) + +type StatsStore struct { + Starts map[string]time.Time + Ends map[string]time.Time + Counter int32 + Mtx sync.Mutex +} + +func NewStatsStore() *StatsStore { + return &StatsStore{ + Starts: make(map[string]time.Time), + Ends: make(map[string]time.Time), + Mtx: sync.Mutex{}, + Counter: 1, + } +} + +func (s *StatsStore) StartTiming(key string) { + s.Mtx.Lock() + defer s.Mtx.Unlock() + s.Starts[key] = time.Now() +} + +func (s *StatsStore) EndTiming(key string) { + s.Mtx.Lock() + defer s.Mtx.Unlock() + s.Ends[key] = time.Now() +} + +func (s *StatsStore) Id() int32 { + return atomic.AddInt32(&s.Counter, 1) +} + +func (s *StatsStore) AsMap() map[string]float64 { + res := make(map[string]float64) + for k, start := range s.Starts { + end := time.Now() + if _, ok := s.Ends[k]; ok { + end = s.Ends[k] + } + dist := end.Sub(start) + res[k] = dist.Seconds() + } + return res +} + +type CLokiQueriable struct { + model.ServiceData + random *rand.Rand + Ctx context.Context + Stats *StatsStore +} + +func (c *CLokiQueriable) Querier(ctx context.Context, mint, maxt int64) (storage.Querier, error) { + if c.random == nil { + c.random = rand.New(rand.NewSource(time.Now().UnixNano())) + } + db, err := c.ServiceData.Session.GetDB(ctx) + if err != nil { + return nil, err + } + return &CLokiQuerier{ + db: db, + ctx: c.Ctx, + }, nil +} + +func (c *CLokiQueriable) SetOidAndDB(ctx context.Context) *CLokiQueriable { + return &CLokiQueriable{ + ServiceData: c.ServiceData, + random: c.random, + Ctx: ctx, + } +} + +type CLokiQuerier struct { + db *model.DataDatabasesMap + ctx context.Context +} + +var supportedFunctions = map[string]bool{ + // Over time + "avg_over_time": true, + "min_over_time": true, + "max_over_time": true, + "sum_over_time": true, + "count_over_time": true, + "quantile_over_time": false, + "stddev_over_time": false, + "stdvar_over_time": false, + "last_over_time": true, + "present_over_time": true, + "absent_over_time": true, + //instant + "": true, + "abs": true, "absent": true, "ceil": true, "exp": true, "floor": true, + "ln": true, "log2": true, "log10": true, "round": true, "scalar": true, + "sgn": true, "sort": true, "sqrt": true, "timestamp": true, "atan": true, + "cos": true, "cosh": true, "sin": true, "sinh": true, "tan": true, + "tanh": true, "deg": true, "rad": true, + //agg + "sum": true, + "min": true, + "max": true, + "group": true, + "avg": true, +} + +func (c *CLokiQuerier) transpileLabelMatchers(hints *storage.SelectHints, + matchers []*labels.Matcher, versionInfo dbVersion.VersionInfo) (*transpiler.TranspileResponse, error) { + isSupported, ok := supportedFunctions[hints.Func] + + useRawData := hints.Start%15000 != 0 || + hints.Step < 15000 || + (hints.Range > 0 && hints.Range < 15000) || + !(isSupported || !ok) + ctx := shared.PlannerContext{ + IsCluster: c.db.Config.ClusterName != "", + From: time.Unix(0, hints.Start*1000000), + To: time.Unix(0, hints.End*1000000), + Ctx: c.ctx, + CHDb: c.db.Session, + CancelCtx: nil, + Step: time.Millisecond * time.Duration(hints.Step), + Type: 2, + VersionInfo: versionInfo, + } + tables.PopulateTableNames(&ctx, c.db) + if useRawData { + return transpiler.TranspileLabelMatchers(hints, &ctx, matchers...) + } + return transpiler.TranspileLabelMatchersDownsample(hints, &ctx, matchers...) +} + +func (c *CLokiQuerier) Select(sortSeries bool, hints *storage.SelectHints, + matchers ...*labels.Matcher) storage.SeriesSet { + + versionInfo, err := dbVersion.GetVersionInfo(c.ctx, c.db.Config.ClusterName != "", c.db.Session) + if err != nil { + return &model.SeriesSet{Error: err} + } + + q, err := c.transpileLabelMatchers(hints, matchers, versionInfo) + if err != nil { + return &model.SeriesSet{Error: err} + } + ctx := sql.Ctx{ + Params: map[string]sql.SQLObject{}, + } + var opts []int + if c.db.Config.ClusterName != "" { + opts = []int{sql.STRING_OPT_INLINE_WITH} + } + str, err := q.Query.String(&ctx, opts...) + if err != nil { + return &model.SeriesSet{Error: err} + } + rows, err := c.db.Session.QueryCtx(c.ctx, str) + if err != nil { + fmt.Println(str) + return &model.SeriesSet{Error: err} + } + var ( + fp uint64 = 0 + val float64 = 0 + ts int64 = 0 + lastLabels uint64 = 0 + ) + res := model.SeriesSet{ + Error: nil, + Series: make([]*model.Series, 0, 1000), + } + res.Reset() + cntRows := 0 + cntSeries := 0 + lblsGetter := newLabelsGetter(time.UnixMilli(hints.Start), time.UnixMilli(hints.End), c.db, c.ctx) + for rows.Next() { + err = rows.Scan(&fp, &val, &ts) + if err != nil { + return &model.SeriesSet{Error: err} + } + if len(res.Series) == 0 || fp != lastLabels { + lblsGetter.Plan(fp) + lastLabels = fp + if len(res.Series) > 0 && q.MapResult != nil { + res.Series[len(res.Series)-1].Samples = q.MapResult(res.Series[len(res.Series)-1].Samples) + } + res.Series = append(res.Series, &model.Series{ + LabelsGetter: lblsGetter, + Fp: fp, + Samples: make([]model.Sample, 0, 500), + }) + cntSeries++ + } + res.Series[len(res.Series)-1].Samples = append(res.Series[len(res.Series)-1].Samples, + model.Sample{ts, val}) + cntRows++ + } + if len(res.Series) > 0 && q.MapResult != nil { + res.Series[len(res.Series)-1].Samples = q.MapResult(res.Series[len(res.Series)-1].Samples) + } + err = lblsGetter.Fetch() + if err != nil { + return &model.SeriesSet{Error: err} + } + c.ReshuffleSeries(res.Series) + sort.Slice(res.Series, func(i, j int) bool { + for k, l1 := range res.Series[i].Labels() { + l2 := res.Series[j].Labels() + if k >= len(l2) { + return false + } + if l1.Name != l2[k].Name { + return l1.Name < l2[k].Name + } + if l1.Value != l2[k].Value { + return l1.Value < l2[k].Value + } + } + return true + }) + return &res +} + +func (c *CLokiQuerier) ReshuffleSeries(series []*model.Series) { + seriesMap := make(map[uint64]*model.Series, len(series)*2) + for _, ent := range series { + labels := ent.LabelsGetter.Get(ent.Fp) + strLabels := make([][]byte, labels.Len()) + for i, lbl := range labels { + strLabels[i] = []byte(lbl.Name + "=" + lbl.Value) + } + str := bytes.Join(strLabels, []byte(" ")) + _fp := cityhash102.CityHash64(str, uint32(len(str))) + if chunk, ok := seriesMap[_fp]; ok { + logger.Error(fmt.Printf("Warning: double labels set found [%d - %d]: %s", + chunk.Fp, ent.Fp, string(str))) + chunk.Samples = append(chunk.Samples, ent.Samples...) + sort.Slice(chunk.Samples, func(i, j int) bool { + return chunk.Samples[i].TimestampMs < chunk.Samples[j].TimestampMs + }) + + } else { + seriesMap[_fp] = ent + } + } +} + +func (c *CLokiQuerier) LabelValues(name string, matchers ...*labels.Matcher) ([]string, storage.Warnings, error) { + return nil, nil, nil +} + +func (c *CLokiQuerier) LabelNames(matchers ...*labels.Matcher) ([]string, storage.Warnings, error) { + return nil, nil, nil +} + +// Close releases the resources of the Querier. +func (c *CLokiQuerier) Close() error { + return nil +} + +type labelsGetter struct { + DateFrom time.Time + DateTo time.Time + Conn *model.DataDatabasesMap + Ctx context.Context + fingerprintsHas map[uint64][][]string + fingerprintToFetch map[uint64]bool + Distributed bool + plugin plugins.LabelsGetterPlugin +} + +func newLabelsGetter(from time.Time, to time.Time, conn *model.DataDatabasesMap, ctx context.Context) *labelsGetter { + res := &labelsGetter{ + DateFrom: from, + DateTo: to, + Conn: conn, + Ctx: ctx, + Distributed: conn.Config.ClusterName != "", + fingerprintsHas: make(map[uint64][][]string), + fingerprintToFetch: make(map[uint64]bool), + } + p := plugins.GetLabelsGetterPlugin() + if p != nil { + res.plugin = *p + } + return res +} + +func (l *labelsGetter) Get(fingerprint uint64) labels.Labels { + strLabels, ok := l.fingerprintsHas[fingerprint] + if !ok { + logger.Error(fmt.Sprintf("Warning: no fingerprint %d found", fingerprint)) + return labels.Labels{} + } + res := make(labels.Labels, len(strLabels)) + for i, label := range strLabels { + res[i] = labels.Label{ + Name: label[0], + Value: label[1], + } + } + sort.Slice(res, func(i, j int) bool { + return res[i].Name < res[j].Name + }) + return res +} + +func (l *labelsGetter) Plan(fingerprint uint64) { + l.fingerprintToFetch[fingerprint] = true +} + +func (l *labelsGetter) getFetchRequest(fingerprints map[uint64]bool) sql.ISelect { + if l.plugin != nil { + return l.plugin.GetLabelsQuery(l.Ctx, l.Conn, fingerprints, l.DateFrom, l.DateTo) + } + tableName := tables.GetTableName("time_series") + if l.Distributed { + tableName = tables.GetTableName("time_series_dist") + } + fps := make([]sql.SQLObject, 0, len(fingerprints)) + for fp, _ := range l.fingerprintToFetch { + fps = append(fps, sql.NewRawObject(strconv.FormatUint(fp, 10))) + } + req := sql.NewSelect(). + Select(sql.NewRawObject("fingerprint"), sql.NewSimpleCol("JSONExtractKeysAndValues(labels, 'String')", "labels")). + From(sql.NewRawObject(tableName)). + AndWhere( + sql.NewIn(sql.NewRawObject("fingerprint"), fps...), + sql.Ge(sql.NewRawObject("date"), sql.NewStringVal(FormatFromDate(l.DateFrom))), + sql.Le(sql.NewRawObject("date"), sql.NewStringVal(l.DateTo.Format("2006-01-02")))) + return req +} + +func (l *labelsGetter) Fetch() error { + if len(l.fingerprintToFetch) == 0 { + return nil + } + req := l.getFetchRequest(l.fingerprintToFetch) + strReq, err := req.String(&sql.Ctx{}) + if err != nil { + return err + } + rows, err := l.Conn.Session.QueryCtx(l.Ctx, strReq) + if err != nil { + return err + } + for rows.Next() { + var ( + fingerprint uint64 + labels [][]interface{} + ) + err := rows.Scan(&fingerprint, &labels) + if err != nil { + return err + } + strLabels := make([][]string, len(labels)) + for i, label := range labels { + strLabels[i] = []string{label[0].(string), label[1].(string)} + } + sort.Slice(strLabels, func(i, j int) bool { + return strings.Compare(strLabels[i][0], strLabels[j][0]) < 0 + }) + l.fingerprintsHas[fingerprint] = strLabels + //cache.Set(l.getIdx(fingerprint), bLabels) + } + return nil +} diff --git a/reader/service/queryLabelsService.go b/reader/service/queryLabelsService.go new file mode 100644 index 00000000..cbc533d2 --- /dev/null +++ b/reader/service/queryLabelsService.go @@ -0,0 +1,366 @@ +package service + +import ( + "context" + "encoding/json" + "fmt" + "github.com/metrico/qryn/reader/logql/logql_parser" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/clickhouse_planner" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/plugins" + "github.com/metrico/qryn/reader/utils/dbVersion" + "github.com/metrico/qryn/reader/utils/logger" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "github.com/metrico/qryn/reader/utils/tables" + "github.com/prometheus/prometheus/model/labels" + "github.com/prometheus/prometheus/promql" + "github.com/prometheus/prometheus/promql/parser" + "strings" + "time" +) + +type QueryLabelsService struct { + model.ServiceData + plugin plugins.QueryLabelsServicePlugin +} + +func NewQueryLabelsService(sd *model.ServiceData) *QueryLabelsService { + p := plugins.GetQueryLabelsServicePlugin() + res := &QueryLabelsService{ + ServiceData: *sd, + } + if p != nil { + (*p).SetServiceData(sd) + res.plugin = *p + } + return res +} + +func (q *QueryLabelsService) GenericLabelReq(ctx context.Context, query string, args ...interface{}) (chan string, error) { + fmt.Println(query) + session, err := q.Session.GetDB(ctx) + if err != nil { + return nil, err + } + rows, err := session.Session.QueryCtx(ctx, query, args...) + if err != nil { + return nil, err + } + res := make(chan string) + go func() { + defer close(res) + defer rows.Close() + res <- `{"status": "success","data": [` + i := 0 + for rows.Next() { + strLbl := "" + err = rows.Scan(&strLbl) + if err != nil { + logger.Error(err) + break + } + qStrLbl, err := json.Marshal(strLbl) + if err != nil { + logger.Error(err) + break + } + if i != 0 { + res <- "," + } + res <- string(qStrLbl) + i++ + } + res <- "]}" + }() + return res, nil +} + +func (q *QueryLabelsService) isDistributed(db *model.DataDatabasesMap) bool { + return db.Config.ClusterName != "" +} + +func (q *QueryLabelsService) GetEstimateKVComplexityRequest(ctx context.Context, + conn *model.DataDatabasesMap) sql.ISelect { + if q.plugin != nil { + return q.plugin.EstimateKVComplexity(ctx, conn) + } + tableName := tables.GetTableName("time_series") + if q.isDistributed(conn) { + tableName = tables.GetTableName("time_series_dist") + } + fpRequest := sql.NewSelect(). + Distinct(true). + Select(sql.NewRawObject("fingerprint")). + From(sql.NewRawObject(tableName)). + Limit(sql.NewRawObject("10001")) + withFpRequest := sql.NewWith(fpRequest, "fp_request") + fpRequest = sql.NewSelect(). + With(withFpRequest). + Select(sql.NewSimpleCol("COUNT(1)", "cnt")). + From(sql.NewWithRef(withFpRequest)) + return fpRequest +} + +func (q *QueryLabelsService) estimateKVComplexity(ctx context.Context) (int64, error) { + conn, err := q.Session.GetDB(ctx) + fpRequest := q.GetEstimateKVComplexityRequest(ctx, conn) + request, err := fpRequest.String(&sql.Ctx{ + Params: map[string]sql.SQLObject{}, + Result: map[string]sql.SQLObject{}, + }) + if err != nil { + return 0, err + } + rows, err := conn.Session.QueryCtx(ctx, request) + if err != nil { + return 0, err + } + defer rows.Close() + rows.Next() + var cpl int64 = 0 + err = rows.Scan(&cpl) + return cpl, err +} + +func (q *QueryLabelsService) Labels(ctx context.Context, startMs int64, endMs int64, labelsType uint16) (chan string, error) { + conn, err := q.Session.GetDB(ctx) + if err != nil { + return nil, err + } + samplesKVTable := tables.GetTableName("time_series_gin") + if conn.Config.ClusterName != "" { + samplesKVTable = tables.GetTableName("time_series_gin_dist") + } + sel := sql.NewSelect().Distinct(true). + Select(sql.NewRawObject("key")). + From(sql.NewSimpleCol(samplesKVTable, "samples")). + AndWhere( + sql.NewIn(sql.NewRawObject("type"), sql.NewIntVal(int64(labelsType)), sql.NewIntVal(int64(0))), + sql.Ge(sql.NewRawObject("date"), + sql.NewStringVal(FormatFromDate(time.Unix(startMs/1000, 0)))), + sql.Le(sql.NewRawObject("date"), + sql.NewStringVal(time.Unix(endMs/1000, 0).UTC().Format("2006-01-02"))), + ) + query, err := sel.String(&sql.Ctx{ + Params: map[string]sql.SQLObject{}, + Result: map[string]sql.SQLObject{}, + }) + return q.GenericLabelReq(ctx, query) +} + +func (q *QueryLabelsService) PromValues(ctx context.Context, label string, match []string, startMs int64, endMs int64, + labelsType uint16) (chan string, error) { + lMatchers := make([]string, len(match)) + var err error + for i, m := range match { + lMatchers[i], err = q.Prom2LogqlMatch(m) + if err != nil { + return nil, err + } + } + return q.Values(ctx, label, lMatchers, startMs, endMs, labelsType) +} + +func (q *QueryLabelsService) Prom2LogqlMatch(match string) (string, error) { + e := promql.NewEngine(promql.EngineOpts{}) + rq, err := e.NewRangeQuery(nil, nil, match, time.Now(), time.Now(), time.Second) + if err != nil { + panic(err) + } + var getMatchers func(node parser.Node) []*labels.Matcher + getMatchers = func(node parser.Node) []*labels.Matcher { + var res []*labels.Matcher + vs, ok := node.(*parser.VectorSelector) + if ok { + return vs.LabelMatchers + } + for _, c := range parser.Children(node) { + res = append(res, getMatchers(c)...) + } + return res + } + matchers := getMatchers(rq.Statement()) + strMatchers := make([]string, len(matchers)) + for i, m := range matchers { + strMatchers[i] = m.String() + } + return fmt.Sprintf("{%s}", strings.Join(strMatchers, ",")), nil +} + +func (q *QueryLabelsService) Values(ctx context.Context, label string, match []string, startMs int64, endMs int64, + labelsType uint16) (chan string, error) { + conn, err := q.Session.GetDB(ctx) + if err != nil { + return nil, err + } + if label == "" { + res := make(chan string, 1) + defer close(res) + res <- "{\"status\": \"success\",\"data\": []}" + return res, nil + } + if err != nil { + return nil, err + } + + var planner shared.SQLRequestPlanner + tsGinTableName := tables.GetTableName("time_series_gin") + //TODO: add pluggable extension + if len(match) > 0 { + planner, err = q.getMultiMatchValuesPlanner(match, label) + if err != nil { + return nil, err + } + } else { + planner = clickhouse_planner.NewValuesPlanner(nil, label) + } + if conn.Config.ClusterName != "" { + tsGinTableName += "_dist" + } + + versionInfo, err := dbVersion.GetVersionInfo(ctx, conn.Config.ClusterName != "", conn.Session) + if err != nil { + return nil, err + } + + plannerCtx := shared.PlannerContext{ + IsCluster: conn.Config.ClusterName != "", + From: time.Unix(startMs/1000, 0), + To: time.Unix(endMs/1000, 0), + Limit: 10000, + UseCache: false, + Ctx: ctx, + CHDb: conn.Session, + CHSqlCtx: nil, + Type: uint8(labelsType), + VersionInfo: versionInfo, + } + tables.PopulateTableNames(&plannerCtx, conn) + plannerCtx.TimeSeriesGinTableName = tsGinTableName + query, err := planner.Process(&plannerCtx) + if err != nil { + return nil, err + } + strQuery, err := query.String(&sql.Ctx{ + Params: map[string]sql.SQLObject{}, + Result: map[string]sql.SQLObject{}, + }) + if err != nil { + return nil, err + } + return q.GenericLabelReq(ctx, strQuery) +} + +func (q *QueryLabelsService) getMultiMatchValuesPlanner(match []string, key string) (shared.SQLRequestPlanner, error) { + matchScripts := make([]*logql_parser.LogQLScript, len(match)) + var err error + for i, m := range match { + matchScripts[i], err = logql_parser.Parse(m) + if err != nil { + return nil, err + } + } + selects := make([]shared.SQLRequestPlanner, len(matchScripts)) + for i, m := range matchScripts { + selects[i], err = logql_transpiler_v2.PlanFingerprints(m) + if err != nil { + return nil, err + } + } + var planner shared.SQLRequestPlanner = &clickhouse_planner.MultiStreamSelectPlanner{selects} + planner = clickhouse_planner.NewValuesPlanner(planner, key) + return planner, nil +} + +func (q *QueryLabelsService) Series(ctx context.Context, requests []string, startMs int64, endMs int64, + labelsType uint16) (chan string, error) { + res := make(chan string) + if requests == nil { + go func() { + defer close(res) + res <- `{"status":"success", "data":[]}` + }() + return res, nil + } + conn, err := q.Session.GetDB(ctx) + if err != nil { + return nil, err + } + planner, err := q.querySeries(requests) + if err != nil { + return nil, err + } + + versionInfo, err := dbVersion.GetVersionInfo(ctx, conn.Config.ClusterName != "", conn.Session) + if err != nil { + return nil, err + } + + plannerCtx := shared.PlannerContext{ + IsCluster: conn.Config.ClusterName != "", + From: time.Unix(startMs/1000, 0), + To: time.Unix(endMs/1000, 0), + Limit: 10000, + Ctx: ctx, + CHDb: conn.Session, + Type: uint8(labelsType), + VersionInfo: versionInfo, + } + tables.PopulateTableNames(&plannerCtx, conn) + req, err := planner.Process(&plannerCtx) + if err != nil { + return nil, err + } + strQuery, err := req.String(&sql.Ctx{ + Params: map[string]sql.SQLObject{}, + Result: map[string]sql.SQLObject{}, + }) + if err != nil { + return nil, err + } + rows, err := conn.Session.QueryCtx(ctx, strQuery) + if err != nil { + return nil, err + } + go func() { + defer rows.Close() + defer close(res) + lbls := "" + i := 0 + res <- `{"status":"success", "data":[` + for rows.Next() { + err = rows.Scan(&lbls) + if err != nil { + logger.Error(err) + break + } + if i != 0 { + res <- "," + } + res <- lbls + i++ + } + res <- `]}` + }() + return res, nil +} + +func (q *QueryLabelsService) querySeries(requests []string) (shared.SQLRequestPlanner, error) { + + fpPlanners := make([]shared.SQLRequestPlanner, len(requests)) + for i, req := range requests { + script, err := logql_parser.ParseSeries(req) + if err != nil { + return nil, err + } + fpPlanners[i], err = logql_transpiler_v2.PlanFingerprints(script) + if err != nil { + return nil, err + } + } + var planner shared.SQLRequestPlanner = &clickhouse_planner.MultiStreamSelectPlanner{Mains: fpPlanners} + planner = clickhouse_planner.NewSeriesPlanner(planner) + return planner, nil +} diff --git a/reader/service/queryRangeService.go b/reader/service/queryRangeService.go new file mode 100644 index 00000000..5c235f56 --- /dev/null +++ b/reader/service/queryRangeService.go @@ -0,0 +1,688 @@ +package service + +import ( + "context" + "encoding/json" + "fmt" + jsoniter "github.com/json-iterator/go" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/plugins" + "github.com/metrico/qryn/reader/utils/dbVersion" + "github.com/metrico/qryn/reader/utils/tables" + "io" + "sort" + "strconv" + "strings" + "time" + + _ "github.com/json-iterator/go" + "github.com/metrico/qryn/reader/utils/logger" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type QueryRangeService struct { + model.ServiceData + plugin plugins.QueryRangeServicePlugin +} + +func NewQueryRangeService(data *model.ServiceData) *QueryRangeService { + res := &QueryRangeService{ + ServiceData: *data, + } + p := plugins.GetQueryRangeServicePlugin() + if p != nil { + (*p).SetServiceData(data) + res.plugin = *p + } + return res +} + +func hashLabels(labels [][]interface{}) string { + _labels := make([]string, len(labels)) + for i, l := range labels { + val, _ := json.Marshal(l[1].(string)) + _labels[i] = fmt.Sprintf("\"%s\":%s", l[0].(string), val) + } + return fmt.Sprintf("{%s}", strings.Join(_labels, ",")) +} + +func hashLabelsMap(labels map[string]string) string { + _labels := make([]string, len(labels)) + i := 0 + for k, v := range labels { + val, _ := json.Marshal(v) + _labels[i] = fmt.Sprintf("\"%s\":%s", k, val) + i++ + } + sort.Strings(_labels) + return fmt.Sprintf("{%s}", strings.Join(_labels, ",")) +} + +func onErr(err error, res chan model.QueryRangeOutput) { + logger.Error(err) + res <- model.QueryRangeOutput{ + Str: "]}}", + Err: err, + } +} + +// func (q *QueryRangeService) exportStreamsValue(out chan []shared.LogEntry, +// +// res chan model.QueryRangeOutput) { +// defer close(res) +// +// res <- model.QueryRangeOutput{Str: `{"status": "success","data": {"resultType": "streams", "result": [`} +// +// var lastFp uint64 +// i := 0 +// j := 0 +// +// for entries := range out { +// for _, e := range entries { +// if e.Err == io.EOF { +// continue +// } +// if e.Err != nil { +// onErr(e.Err, res) +// return +// } +// if lastFp != e.Fingerprint { +// if i > 0 { +// res <- model.QueryRangeOutput{Str: "]},"} +// } +// lastFp = e.Fingerprint +// i = 1 +// j = 0 +// stream, _ := json.Marshal(e.Labels) +// res <- model.QueryRangeOutput{Str: fmt.Sprintf(`{%s:%s, %s: [`, +// strconv.Quote("stream"), string(stream), strconv.Quote("values"))} +// } +// if j > 0 { +// res <- model.QueryRangeOutput{Str: ","} +// } +// j = 1 +// msg, err := json.Marshal(e.Message) +// if err != nil { +// msg = []byte("error string") +// } +// res <- model.QueryRangeOutput{ +// Str: fmt.Sprintf(`["%d", %s]`, e.TimestampNS, msg), +// } +// } +// } +// +// if i > 0 { +// res <- model.QueryRangeOutput{Str: "]}"} +// } +// res <- model.QueryRangeOutput{Str: "]}}"} +// } +func (q *QueryRangeService) exportStreamsValue(out chan []shared.LogEntry, + res chan model.QueryRangeOutput) { + defer close(res) + + json := jsoniter.ConfigFastest + stream := json.BorrowStream(nil) + defer json.ReturnStream(stream) + + // Write initial part of response + stream.WriteObjectStart() + stream.WriteObjectField("status") + stream.WriteString("success") + stream.WriteMore() + stream.WriteObjectField("data") + stream.WriteObjectStart() + stream.WriteObjectField("resultType") + stream.WriteString("streams") + stream.WriteMore() + stream.WriteObjectField("result") + stream.WriteArrayStart() + + res <- model.QueryRangeOutput{Str: string(stream.Buffer())} + stream.Reset(nil) + + var lastFp uint64 + i := 0 + j := 0 + + for entries := range out { + for _, e := range entries { + if e.Err == io.EOF { + continue + } + if e.Err != nil { + onErr(e.Err, res) + return + } + if lastFp != e.Fingerprint { + if i > 0 { + // Close previous stream entry + stream.WriteArrayEnd() + stream.WriteObjectEnd() + stream.WriteMore() + res <- model.QueryRangeOutput{Str: string(stream.Buffer())} + stream.Reset(nil) + } + lastFp = e.Fingerprint + i = 1 + j = 0 + + // Write new stream entry + stream.WriteObjectStart() + stream.WriteObjectField("stream") + writeMap(stream, e.Labels) + stream.WriteMore() + stream.WriteObjectField("values") + stream.WriteArrayStart() + } + if j > 0 { + stream.WriteMore() + } + j = 1 + + // Write value entry + stream.WriteArrayStart() + stream.WriteString(fmt.Sprintf("%d", e.TimestampNS)) + stream.WriteMore() + stream.WriteString(e.Message) + stream.WriteArrayEnd() + + res <- model.QueryRangeOutput{Str: string(stream.Buffer())} + stream.Reset(nil) + } + } + + if i > 0 { + // Close last stream entry + stream.WriteArrayEnd() + stream.WriteObjectEnd() + } + + // Close result array and response object + stream.WriteArrayEnd() + stream.WriteObjectEnd() + stream.WriteObjectEnd() + + res <- model.QueryRangeOutput{Str: string(stream.Buffer())} +} + +func (q *QueryRangeService) QueryRange(ctx context.Context, query string, fromNs int64, toNs int64, stepMs int64, + limit int64, forward bool) (chan model.QueryRangeOutput, error) { + out, isMatrix, err := q.prepareOutput(ctx, query, fromNs, toNs, stepMs, limit, forward) + if err != nil { + return nil, err + } + res := make(chan model.QueryRangeOutput) + + if !isMatrix { + go func() { + q.exportStreamsValue(out, res) + }() + return res, nil + } + go func() { + defer close(res) + + json := jsoniter.ConfigFastest + stream := json.BorrowStream(nil) + defer json.ReturnStream(stream) + + // Write initial part of response + stream.WriteObjectStart() + stream.WriteObjectField("status") + stream.WriteString("success") + stream.WriteMore() + stream.WriteObjectField("data") + stream.WriteObjectStart() + stream.WriteObjectField("resultType") + stream.WriteString("matrix") + stream.WriteMore() + stream.WriteObjectField("result") + stream.WriteArrayStart() + + res <- model.QueryRangeOutput{Str: string(stream.Buffer())} + stream.Reset(nil) + + var lastFp uint64 + i := 0 + j := 0 + + for entries := range out { + for _, e := range entries { + if e.Err != nil && e.Err != io.EOF { + onErr(e.Err, res) + return + } + if e.Err == io.EOF { + break + } + if i == 0 || lastFp != e.Fingerprint { + if i > 0 { + + //]}, + // Close previous metric entry + stream.WriteArrayEnd() + stream.WriteObjectEnd() + stream.WriteMore() + res <- model.QueryRangeOutput{Str: string(stream.Buffer())} + stream.Reset(nil) + } + lastFp = e.Fingerprint + i = 1 + j = 0 + + // Write new metric entry + stream.WriteObjectStart() + stream.WriteObjectField("metric") + writeMap(stream, e.Labels) + stream.WriteMore() + stream.WriteObjectField("values") + stream.WriteArrayStart() + } + if j > 0 { + stream.WriteMore() + } + j = 1 + + // Format value + val := strconv.FormatFloat(e.Value, 'f', -1, 64) + if strings.Contains(val, ".") { + val = strings.TrimSuffix(val, "0") + val = strings.TrimSuffix(val, ".") + } + + // Write value entry + stream.WriteArrayStart() + // Intentional WriteRaw to fix precision in response + stream.WriteRaw(fmt.Sprintf("%f", float64(e.TimestampNS)/1e9)) + stream.WriteMore() + stream.WriteString(val) + stream.WriteArrayEnd() + + res <- model.QueryRangeOutput{Str: string(stream.Buffer())} + stream.Reset(nil) + } + } + + if i > 0 { + // Close last metric entry + stream.WriteArrayEnd() + stream.WriteObjectEnd() + } + + // Close result array and response object + stream.WriteArrayEnd() + stream.WriteObjectEnd() + stream.WriteObjectEnd() + + res <- model.QueryRangeOutput{Str: string(stream.Buffer())} + }() + return res, nil +} + +//func (q *QueryRangeService) QueryRange(ctx context.Context, query string, fromNs int64, toNs int64, stepMs int64, +// limit int64, forward bool) (chan model.QueryRangeOutput, error) { +// out, isMatrix, err := q.prepareOutput(ctx, query, fromNs, toNs, stepMs, limit, forward) +// if err != nil { +// return nil, err +// } +// res := make(chan model.QueryRangeOutput) +// +// if !isMatrix { +// go func() { +// q.exportStreamsValue(out, res) +// }() +// return res, nil +// } +// go func() { +// defer close(res) +// +// res <- model.QueryRangeOutput{Str: `{"status": "success","data": {"resultType": "matrix", "result": [`} +// +// var lastFp uint64 +// i := 0 +// j := 0 +// +// for entries := range out { +// for _, e := range entries { +// if e.Err != nil && e.Err != io.EOF { +// onErr(e.Err, res) +// return +// } +// if e.Err == io.EOF { +// break +// } +// if i == 0 || lastFp != e.Fingerprint { +// if i > 0 { +// res <- model.QueryRangeOutput{Str: "]},"} +// } +// lastFp = e.Fingerprint +// i = 1 +// j = 0 +// stream, _ := json.Marshal(e.Labels) +// res <- model.QueryRangeOutput{Str: fmt.Sprintf(`{%s:%s, %s: [`, +// strconv.Quote("metric"), string(stream), strconv.Quote("values"))} +// } +// if j > 0 { +// res <- model.QueryRangeOutput{Str: ","} +// } +// j = 1 +// val := strconv.FormatFloat(e.Value, 'f', -1, 64) +// if strings.Contains(val, ".") { +// val := strings.TrimSuffix(val, "0") +// val = strings.TrimSuffix(val, ".") +// } +// +// res <- model.QueryRangeOutput{ +// Str: fmt.Sprintf(`[%f, "%s"]`, float64(e.TimestampNS)/1e9, val), +// } +// } +// } +// +// if i > 0 { +// res <- model.QueryRangeOutput{Str: "]}"} +// } +// res <- model.QueryRangeOutput{Str: "]}}"} +// }() +// return res, nil +//} + +func (q *QueryRangeService) prepareOutput(ctx context.Context, query string, fromNs int64, toNs int64, stepMs int64, + limit int64, forward bool) (chan []shared.LogEntry, bool, error) { + conn, err := q.Session.GetDB(ctx) + if err != nil { + return nil, false, err + } + chain, err := logql_transpiler_v2.Transpile(query) + if err != nil { + return nil, false, err + } + versionInfo, err := dbVersion.GetVersionInfo(ctx, conn.Config.ClusterName != "", conn.Session) + if err != nil { + return nil, false, err + } + + _ctx, cancel := context.WithCancel(ctx) + + plannerCtx := tables.PopulateTableNames(&shared.PlannerContext{ + IsCluster: conn.Config.ClusterName != "", + From: time.Unix(fromNs/1000000000, 0), + To: time.Unix(toNs/1000000000, 0), + OrderASC: forward, + Limit: int64(limit), + Ctx: _ctx, + CancelCtx: cancel, + CHDb: conn.Session, + CHFinalize: true, + Step: time.Duration(stepMs) * time.Millisecond, + CHSqlCtx: &sql.Ctx{ + Params: map[string]sql.SQLObject{}, + Result: map[string]sql.SQLObject{}, + }, + VersionInfo: versionInfo, + }, conn) + res, err := chain[0].Process(plannerCtx, nil) + return res, chain[0].IsMatrix(), err +} +func (q *QueryRangeService) QueryInstant(ctx context.Context, query string, timeNs int64, stepMs int64, + limit int64) (chan model.QueryRangeOutput, error) { + out, isMatrix, err := q.prepareOutput(ctx, query, timeNs-300000000000, timeNs, stepMs, limit, false) + if err != nil { + return nil, err + } + res := make(chan model.QueryRangeOutput) + if !isMatrix { + go func() { + q.exportStreamsValue(out, res) + }() + return res, nil + } + + go func() { + defer close(res) + json := jsoniter.ConfigFastest + stream := json.BorrowStream(nil) + defer json.ReturnStream(stream) + + stream.WriteObjectStart() + stream.WriteObjectField("status") + stream.WriteString("success") + stream.WriteMore() + stream.WriteObjectField("data") + stream.WriteObjectStart() + stream.WriteObjectField("resultType") + stream.WriteString("vector") + stream.WriteMore() + stream.WriteObjectField("result") + stream.WriteArrayStart() + + res <- model.QueryRangeOutput{Str: string(stream.Buffer())} + stream.Reset(nil) + i := 0 + lastValues := make(map[uint64]shared.LogEntry) + for entries := range out { + for _, e := range entries { + if e.Err != nil && e.Err != io.EOF { + onErr(e.Err, res) + return + } + if e.Err == io.EOF { + break + } + if _, ok := lastValues[e.Fingerprint]; !ok { + lastValues[e.Fingerprint] = e + continue + } + if lastValues[e.Fingerprint].TimestampNS < e.TimestampNS { + lastValues[e.Fingerprint] = e + continue + } + } + } + for _, e := range lastValues { + if i > 0 { + stream.WriteMore() + } + stream.WriteObjectStart() + stream.WriteObjectField("metric") + stream.WriteObjectStart() + j := 0 + for k, v := range e.Labels { + if j > 0 { + stream.WriteMore() + } + stream.WriteObjectField(k) + stream.WriteString(v) + j++ + } + stream.WriteObjectEnd() + stream.WriteMore() + + val := strconv.FormatFloat(e.Value, 'f', -1, 64) + if strings.Contains(val, ".") { + val := strings.TrimSuffix(val, "0") + val = strings.TrimSuffix(val, ".") + } + + stream.WriteObjectField("value") + stream.WriteArrayStart() + stream.WriteInt64(e.TimestampNS / 1000000000) + stream.WriteMore() + stream.WriteString(val) + stream.WriteArrayEnd() + stream.WriteObjectEnd() + res <- model.QueryRangeOutput{Str: string(stream.Buffer())} + stream.Reset(nil) + i++ + } + stream.WriteArrayEnd() + stream.WriteObjectEnd() + stream.WriteObjectEnd() + res <- model.QueryRangeOutput{Str: string(stream.Buffer())} + }() + + return res, nil +} + +func (q *QueryRangeService) Tail(ctx context.Context, query string) (model.IWatcher, error) { + if q.plugin != nil { + return q.plugin.Tail(ctx, query) + } + + conn, err := q.Session.GetDB(ctx) + if err != nil { + return nil, err + } + sqlQuery, err := logql_transpiler_v2.Transpile(query) + if err != nil { + return nil, err + } + + res := NewWatcher(make(chan model.QueryRangeOutput)) + + from := time.Now().Add(time.Minute * -5) + + _ctx, cancel := context.WithCancel(ctx) + + go func() { + ticker := time.NewTicker(time.Second) + defer cancel() + defer close(res.GetRes()) + defer ticker.Stop() + json := jsoniter.ConfigFastest + + stream := json.BorrowStream(nil) + defer json.ReturnStream(stream) + for _ = range ticker.C { + versionInfo, err := dbVersion.GetVersionInfo(ctx, conn.Config.ClusterName != "", conn.Session) + if err != nil { + logger.Error(err) + return + } + + select { + case <-res.Done(): + return + default: + } + + out, err := sqlQuery[0].Process(tables.PopulateTableNames(&shared.PlannerContext{ + IsCluster: conn.Config.ClusterName != "", + From: from, + To: time.Now(), + OrderASC: false, + Limit: 0, + Ctx: _ctx, + CHDb: conn.Session, + CHFinalize: true, + CHSqlCtx: &sql.Ctx{ + Params: map[string]sql.SQLObject{}, + Result: map[string]sql.SQLObject{}, + }, + CancelCtx: cancel, + VersionInfo: versionInfo, + }, conn), nil) + if err != nil { + logger.Error(err) + return + } + var lastFp uint64 + i := 0 + j := 0 + stream.WriteObjectStart() + stream.WriteObjectField("streams") + stream.WriteArrayStart() + for entries := range out { + for _, e := range entries { + if e.Err == io.EOF { + continue + } + if e.Err != nil { + onErr(e.Err, res.GetRes()) + return + } + if lastFp != e.Fingerprint { + if i > 0 { + stream.WriteArrayEnd() + stream.WriteObjectEnd() + stream.WriteMore() + } + lastFp = e.Fingerprint + i = 1 + j = 0 + + stream.WriteObjectStart() + stream.WriteObjectField("stream") + writeMap(stream, e.Labels) + stream.WriteMore() + stream.WriteObjectField("values") + stream.WriteArrayStart() + } + if j > 0 { + stream.WriteMore() + } + j = 1 + stream.WriteArrayStart() + stream.WriteString(fmt.Sprintf("%d", e.TimestampNS)) + stream.WriteMore() + stream.WriteString(e.Message) + stream.WriteArrayEnd() + if from.UnixNano() < e.TimestampNS { + from = time.Unix(0, e.TimestampNS+1) + } + } + } + if i > 0 { + stream.WriteArrayEnd() + stream.WriteObjectEnd() + } + stream.WriteArrayEnd() + stream.WriteObjectEnd() + res.GetRes() <- model.QueryRangeOutput{Str: string(stream.Buffer())} + stream.Reset(nil) + } + }() + return res, nil +} + +type Watcher struct { + res chan model.QueryRangeOutput + ctx context.Context + cancel context.CancelFunc +} + +func NewWatcher(res chan model.QueryRangeOutput) model.IWatcher { + ctx, cancel := context.WithCancel(context.Background()) + return &Watcher{ + res: res, + ctx: ctx, + cancel: cancel, + } +} + +func (w *Watcher) Done() <-chan struct{} { + return w.ctx.Done() +} + +func (w *Watcher) GetRes() chan model.QueryRangeOutput { + return w.res +} + +func (w *Watcher) Close() { + w.cancel() +} + +func writeMap(stream *jsoniter.Stream, m map[string]string) { + i := 0 + stream.WriteObjectStart() + for k, v := range m { + if i > 0 { + stream.WriteMore() + } + stream.WriteObjectField(k) + stream.WriteString(v) + i++ + } + stream.WriteObjectEnd() +} diff --git a/reader/service/serviceService.go b/reader/service/serviceService.go new file mode 100644 index 00000000..6d43c336 --- /dev/null +++ b/reader/service/serviceService.go @@ -0,0 +1 @@ +package service diff --git a/reader/service/shared.go b/reader/service/shared.go new file mode 100644 index 00000000..6d43c336 --- /dev/null +++ b/reader/service/shared.go @@ -0,0 +1 @@ +package service diff --git a/reader/service/tempoService.go b/reader/service/tempoService.go new file mode 100644 index 00000000..b3c111ca --- /dev/null +++ b/reader/service/tempoService.go @@ -0,0 +1,596 @@ +package service + +import ( + "context" + sql2 "database/sql" + "encoding/hex" + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/plugins" + "github.com/metrico/qryn/reader/tempo" + traceql_parser "github.com/metrico/qryn/reader/traceql/parser" + traceql_transpiler "github.com/metrico/qryn/reader/traceql/transpiler" + "github.com/metrico/qryn/reader/utils/dbVersion" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "github.com/metrico/qryn/reader/utils/tables" + "github.com/valyala/fastjson" + common "go.opentelemetry.io/proto/otlp/common/v1" + v1 "go.opentelemetry.io/proto/otlp/trace/v1" + "google.golang.org/protobuf/proto" + "strings" + "time" +) + +type zipkinPayload struct { + payload string + startTimeNs int64 + durationNs int64 + traceId string + spanId string + payloadType int + parentId string +} + +type TempoService struct { + model.ServiceData + plugin plugins.TempoServicePlugin +} + +func NewTempoService(data model.ServiceData) model.ITempoService { + var p plugins.TempoServicePlugin + _p := plugins.GetTempoServicePlugin() + if _p != nil { + p = *_p + } + return &TempoService{ + ServiceData: data, + plugin: p, + } +} + +func (t *TempoService) GetQueryRequest(ctx context.Context, startNS int64, endNS int64, traceId []byte, + conn *model.DataDatabasesMap) sql.ISelect { + if t.plugin != nil { + return t.plugin.GetQueryRequest(ctx, startNS, endNS, traceId, conn) + } + tableName := tables.GetTableName("tempo_traces") + if conn.Config.ClusterName != "" { + tableName = tables.GetTableName("tempo_traces_dist") + } + oRequest := sql.NewSelect(). + Select( + sql.NewRawObject("trace_id"), + sql.NewRawObject("span_id"), + sql.NewRawObject("parent_id"), + sql.NewRawObject("timestamp_ns"), + sql.NewRawObject("duration_ns"), + sql.NewRawObject("payload_type"), + sql.NewRawObject("payload")). + From(sql.NewRawObject(tableName)). + AndWhere( + sql.Eq(sql.NewRawObject("trace_id"), sql.NewCustomCol( + func(ctx *sql.Ctx, options ...int) (string, error) { + strTraceId, err := sql.NewStringVal(string(traceId)).String(ctx, options...) + if err != nil { + return "", err + } + return fmt.Sprintf("unhex(%s)", strTraceId), nil + }), + )). + OrderBy(sql.NewRawObject("timestamp_ns")). + Limit(sql.NewIntVal(2000)) + if startNS != 0 { + oRequest = oRequest.AndWhere(sql.Ge(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(startNS))) + } + if endNS != 0 { + oRequest = oRequest.AndWhere(sql.Lt(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(endNS))) + } + witORequest := sql.NewWith(oRequest, "raw") + oRequest = sql.NewSelect().With(witORequest). + Select( + sql.NewRawObject("trace_id"), + sql.NewRawObject("span_id"), + sql.NewRawObject("parent_id"), + sql.NewRawObject("timestamp_ns"), + sql.NewRawObject("duration_ns"), + sql.NewRawObject("payload_type"), + sql.NewRawObject("payload")). + From(sql.NewWithRef(witORequest)). + OrderBy(sql.NewOrderBy(sql.NewRawObject("timestamp_ns"), sql.ORDER_BY_DIRECTION_ASC)) + return oRequest +} + +func (t *TempoService) OutputQuery(binIds bool, rows *sql2.Rows) (chan *model.SpanResponse, error) { + res := make(chan *model.SpanResponse) + go func() { + defer close(res) + parser := fastjson.Parser{} + for rows.Next() { + var zipkin zipkinPayload + err := rows.Scan(&zipkin.traceId, &zipkin.spanId, &zipkin.parentId, + &zipkin.startTimeNs, &zipkin.durationNs, &zipkin.payloadType, &zipkin.payload) + if err != nil { + fmt.Println(err) + return + } + var ( + span *v1.Span + serviceName string + ) + switch zipkin.payloadType { + case 1: + span, serviceName, err = parseZipkinJSON(&zipkin, &parser, binIds) + case 2: + span, serviceName, err = parseOTLP(&zipkin) + } + if err != nil { + fmt.Println(err) + return + } + res <- &model.SpanResponse{ + span, serviceName, + } + } + }() + return res, nil +} + +func (t *TempoService) Query(ctx context.Context, startNS int64, endNS int64, traceId []byte, + binIds bool) (chan *model.SpanResponse, error) { + conn, err := t.Session.GetDB(ctx) + if err != nil { + return nil, err + } + oRequest := t.GetQueryRequest(ctx, startNS, endNS, traceId, conn) + request, err := oRequest.String(&sql.Ctx{ + Params: map[string]sql.SQLObject{}, + Result: map[string]sql.SQLObject{}, + }) + rows, err := conn.Session.QueryCtx(ctx, request) + if err != nil { + return nil, err + } + return t.OutputQuery(binIds, rows) +} + +func (t *TempoService) GetTagsRequest(ctx context.Context, conn *model.DataDatabasesMap) sql.ISelect { + tableName := tables.GetTableName("tempo_traces_kv") + if conn.Config.ClusterName != "" { + tableName = tables.GetTableName("tempo_traces_kv_dist") + } + oQuery := sql.NewSelect(). + Distinct(true). + Select(sql.NewRawObject("key")). + From(sql.NewRawObject(tableName)). + OrderBy(sql.NewRawObject("key")) + return oQuery +} + +func (t *TempoService) Tags(ctx context.Context) (chan string, error) { + conn, err := t.Session.GetDB(ctx) + if err != nil { + return nil, err + } + oQuery := t.GetTagsRequest(ctx, conn) + query, err := oQuery.String(&sql.Ctx{ + Params: map[string]sql.SQLObject{}, + Result: map[string]sql.SQLObject{}, + }) + rows, err := conn.Session.QueryCtx(ctx, query) + if err != nil { + return nil, err + } + res := make(chan string) + go func() { + defer close(res) + for rows.Next() { + var k string + err = rows.Scan(&k) + if err != nil { + return + } + res <- k + } + }() + return res, nil +} + +func (t *TempoService) TagsV2(ctx context.Context, query string, from time.Time, to time.Time, + limit int) (chan string, error) { + conn, err := t.Session.GetDB(ctx) + if err != nil { + return nil, err + } + var oScript *traceql_parser.TraceQLScript + if query != "" { + oScript, err = traceql_parser.Parse(query) + if err != nil { + return nil, err + } + } + + planCtx := shared.PlannerContext{ + IsCluster: conn.Config.ClusterName != "", + From: from, + To: to, + Limit: int64(limit), + CHDb: conn.Session, + Ctx: ctx, + } + + tables.PopulateTableNames(&planCtx, conn) + + planner, err := traceql_transpiler.PlanTagsV2(oScript) + if err != nil { + return nil, err + } + + req, err := planner.Process(&planCtx) + if err != nil { + return nil, err + } + + res := make(chan string) + go func() { + defer close(res) + for tags := range req { + for _, value := range tags { + res <- value + } + } + }() + + return res, nil +} + +func (t *TempoService) ValuesV2(ctx context.Context, key string, query string, from time.Time, to time.Time, + limit int) (chan string, error) { + conn, err := t.Session.GetDB(ctx) + if err != nil { + return nil, err + } + var oScript *traceql_parser.TraceQLScript + if query != "" { + oScript, err = traceql_parser.Parse(query) + if err != nil { + return nil, err + } + } + + planCtx := shared.PlannerContext{ + IsCluster: conn.Config.ClusterName != "", + From: from, + To: to, + Limit: int64(limit), + CHDb: conn.Session, + Ctx: ctx, + } + + tables.PopulateTableNames(&planCtx, conn) + + planner, err := traceql_transpiler.PlanValuesV2(oScript, key) + if err != nil { + return nil, err + } + + req, err := planner.Process(&planCtx) + if err != nil { + return nil, err + } + + res := make(chan string) + go func() { + defer close(res) + for tags := range req { + for _, value := range tags { + res <- value + } + } + }() + + return res, nil +} + +func (t *TempoService) GetValuesRequest(ctx context.Context, tag string, conn *model.DataDatabasesMap) sql.ISelect { + tableName := tables.GetTableName("tempo_traces_kv") + if conn.Config.ClusterName != "" { + tableName = tables.GetTableName("tempo_traces_kv_dist") + } + oRequest := sql.NewSelect(). + Distinct(true). + Select(sql.NewRawObject("val")). + From(sql.NewRawObject(tableName)). + AndWhere(sql.Eq(sql.NewRawObject("key"), sql.NewStringVal(tag))). + OrderBy(sql.NewRawObject("val")) + return oRequest +} + +func (t *TempoService) Values(ctx context.Context, tag string) (chan string, error) { + conn, err := t.Session.GetDB(ctx) + if err != nil { + return nil, err + } + if strings.HasPrefix(tag, "span.") { + tag = tag[5:] + } + if strings.HasPrefix(tag, ".") { + tag = tag[1:] + } + if len(tag) >= 10 && strings.HasPrefix(tag, "resource.") { + tag = tag[9:] + } + oRequest := t.GetValuesRequest(ctx, tag, conn) + query, err := oRequest.String(&sql.Ctx{ + Params: map[string]sql.SQLObject{}, + Result: map[string]sql.SQLObject{}, + }) + if err != nil { + return nil, err + } + rows, err := conn.Session.QueryCtx(ctx, query) + if err != nil { + return nil, err + } + res := make(chan string) + go func() { + defer close(res) + for rows.Next() { + var v string + err = rows.Scan(&v) + if err != nil { + return + } + res <- v + } + }() + return res, nil +} + +func (t *TempoService) Search(ctx context.Context, + tags string, minDurationNS int64, maxDurationNS int64, limit int, fromNS int64, toNS int64) (chan *model.TraceResponse, error) { + conn, err := t.Session.GetDB(ctx) + if err != nil { + return nil, err + } + var idxQuery *tempo.SQLIndexQuery = nil + distributed := conn.Config.ClusterName != "" + if tags != "" { + ver, err := dbVersion.GetVersionInfo(ctx, distributed, conn.Session) + if err != nil { + return nil, err + } + idxQuery = &tempo.SQLIndexQuery{ + Tags: tags, + Ctx: ctx, + FromNS: fromNS, + ToNS: toNS, + MinDurationNS: minDurationNS, + MaxDurationNS: maxDurationNS, + Distributed: false, + Database: conn.Config.Name, + Ver: ver, + Limit: int64(limit), + } + } + request, err := tempo.GetTracesQuery(ctx, idxQuery, limit, fromNS, toNS, distributed, minDurationNS, maxDurationNS) + if err != nil { + return nil, err + } + strRequest, err := request.String(&sql.Ctx{}) + rows, err := conn.Session.QueryCtx(ctx, strRequest) + if err != nil { + return nil, err + } + res := make(chan *model.TraceResponse) + go func() { + defer close(res) + for rows.Next() { + row := model.TraceResponse{} + err = rows.Scan(&row.TraceID, + &row.RootServiceName, + &row.RootTraceName, + &row.StartTimeUnixNano, + &row.DurationMs) + if err != nil { + fmt.Println(err) + return + } + res <- &row + } + }() + return res, nil +} + +func decodeParentId(parentId []byte) ([]byte, error) { + if len(parentId) < 16 { + return nil, nil + } + if len(parentId) > 16 { + return nil, fmt.Errorf("parent id is too big") + } + res := make([]byte, 8) + _, err := hex.Decode(res, parentId) + return res, err +} + +func parseZipkinJSON(payload *zipkinPayload, parser *fastjson.Parser, binIds bool) (*v1.Span, string, error) { + root, err := parser.Parse(payload.payload) + if err != nil { + return nil, "", err + } + kind := v1.Span_SPAN_KIND_UNSPECIFIED + switch string(root.GetStringBytes("kind")) { + case "CLIENT": + kind = v1.Span_SPAN_KIND_CLIENT + case "SERVER": + kind = v1.Span_SPAN_KIND_SERVER + case "PRODUCER": + kind = v1.Span_SPAN_KIND_PRODUCER + case "CONSUMER": + kind = v1.Span_SPAN_KIND_CONSUMER + } + traceId := payload.traceId + /*if binIds { + _traceId := make([]byte, 32) + _, err := hex.Decode(_traceId, traceId) + if err != nil { + fmt.Println(traceId) + fmt.Println(err) + return nil, "", err + } + traceId = _traceId + }*/ + id := payload.spanId + /*if binIds { + _id := make([]byte, 16) + _, err := hex.Decode(_id, id) + if err != nil { + fmt.Println(id) + fmt.Println(err) + return nil, "", err + } + id = _id + }*/ + span := v1.Span{ + TraceId: []byte(traceId[:16]), + SpanId: []byte(id[:8]), + TraceState: "", + ParentSpanId: nil, + Name: string(root.GetStringBytes("name")), + Kind: kind, + StartTimeUnixNano: uint64(payload.startTimeNs), + EndTimeUnixNano: uint64(payload.startTimeNs + payload.durationNs), + Attributes: make([]*common.KeyValue, 0, 10), + DroppedAttributesCount: 0, + Events: make([]*v1.Span_Event, 0, 10), + DroppedEventsCount: 0, + Links: nil, + DroppedLinksCount: 0, + Status: nil, // todo we set status here. + } + parentId := root.GetStringBytes("parentId") + if parentId != nil { + bParentId, err := decodeParentId(parentId) + if err == nil { + span.ParentSpanId = bParentId + } + } + attrs := root.GetObject("tags") + serviceName := "" + if attrs != nil { + attrs.Visit(func(key []byte, v *fastjson.Value) { + if v.Type() != fastjson.TypeString { + return + } + span.Attributes = append(span.Attributes, &common.KeyValue{ + Key: string(key), + Value: &common.AnyValue{ + Value: &common.AnyValue_StringValue{StringValue: string(v.GetStringBytes())}, + }, + }) + }) + } + for _, endpoint := range []string{"localEndpoint", "remoteEndpoint"} { + ep := root.GetObject(endpoint) + if ep == nil { + continue + } + for _, attr := range []string{"serviceName", "ipv4", "ipv6"} { + _val := ep.Get(attr) + if _val == nil || _val.Type() != fastjson.TypeString { + continue + } + if serviceName == "" && attr == "serviceName" { + serviceName = string(_val.GetStringBytes()) + } + span.Attributes = append(span.Attributes, &common.KeyValue{ + Key: endpoint + "." + attr, + Value: &common.AnyValue{ + Value: &common.AnyValue_StringValue{StringValue: string(_val.GetStringBytes())}, + }, + }) + } + port := root.GetInt64(endpoint, "port") + if port != 0 { + span.Attributes = append(span.Attributes, &common.KeyValue{ + Key: endpoint + ".port", + Value: &common.AnyValue{ + Value: &common.AnyValue_IntValue{IntValue: port}, + }, + }) + } + } + span.Attributes = append(span.Attributes, &common.KeyValue{ + Key: "service.name", + Value: &common.AnyValue{Value: &common.AnyValue_StringValue{StringValue: serviceName}}, + }) + for _, anno := range root.GetArray("annotations") { + ts := anno.GetUint64("timestamp") * 1000 + if ts == 0 { + continue + } + span.Events = append(span.Events, &v1.Span_Event{ + TimeUnixNano: ts, + Name: string(anno.GetStringBytes("value")), + }) + } + + if span.Status == nil { + span.Status = &v1.Status{ + Code: v1.Status_STATUS_CODE_UNSET, + } + } + return &span, serviceName, nil +} + +func parseOTLP(payload *zipkinPayload) (*v1.Span, string, error) { + var ( + span *v1.Span + err error + ) + if payload.payload[0] == '{' { + span, err = parseOTLPJson(payload) + } else { + span, err = parseOTLPPB(payload) + } + if err != nil { + return nil, "", err + } + firstLevelMap := make(map[string]*common.KeyValue) + for _, kv := range span.Attributes { + firstLevelMap[kv.Key] = kv + } + serviceName := "" + for _, attr := range []string{"peer.service", "service.name", "faas.name", + "k8s.deployment.name", "process.executable.name"} { + if val, ok := firstLevelMap[attr]; ok && val.Value.GetStringValue() != "" { + serviceName = val.Value.GetStringValue() + break + } + } + if serviceName == "" { + serviceName = "OTLPResourceNoServiceName" + } + firstLevelMap["service.name"] = &common.KeyValue{ + Key: "service.name", + Value: &common.AnyValue{Value: &common.AnyValue_StringValue{StringValue: serviceName}}, + } + span.Attributes = make([]*common.KeyValue, 0, len(firstLevelMap)) + for _, kv := range firstLevelMap { + span.Attributes = append(span.Attributes, kv) + } + if span.Status == nil { + span.Status = &v1.Status{ + Code: v1.Status_STATUS_CODE_UNSET, + } + } + return span, serviceName, nil + +} + +func parseOTLPPB(payload *zipkinPayload) (*v1.Span, error) { + span := &v1.Span{} + err := proto.Unmarshal([]byte(payload.payload), span) + return span, err +} diff --git a/reader/service/tempoServiceTraceQL.go b/reader/service/tempoServiceTraceQL.go new file mode 100644 index 00000000..bbff21ee --- /dev/null +++ b/reader/service/tempoServiceTraceQL.go @@ -0,0 +1,69 @@ +package service + +import ( + "context" + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/model" + traceql_parser "github.com/metrico/qryn/reader/traceql/parser" + traceql_transpiler "github.com/metrico/qryn/reader/traceql/transpiler" + "github.com/metrico/qryn/reader/utils/dbVersion" + "time" +) + +func (t *TempoService) SearchTraceQL(ctx context.Context, + q string, limit int, from time.Time, to time.Time) (chan []model.TraceInfo, error) { + conn, err := t.Session.GetDB(ctx) + if err != nil { + return nil, err + } + script, err := traceql_parser.Parse(q) + if err != nil { + return nil, err + } + planner, err := traceql_transpiler.Plan(script) + if err != nil { + return nil, err + } + versionInfo, err := dbVersion.GetVersionInfo(ctx, conn.Config.ClusterName != "", conn.Session) + if err != nil { + return nil, err + } + + ctx, cancel := context.WithCancel(ctx) + + var ( + tracesAttrsTable = fmt.Sprintf("`%s`.tempo_traces_attrs_gin", conn.Config.Name) + tracesAttrsDistTable = fmt.Sprintf("`%s`.tempo_traces_attrs_gin_dist", conn.Config.Name) + tracesTable = fmt.Sprintf("`%s`.tempo_traces", conn.Config.Name) + tracesDistTable = fmt.Sprintf("`%s`.tempo_traces_dist", conn.Config.Name) + ) + + ch, err := planner.Process(&shared.PlannerContext{ + IsCluster: conn.Config.ClusterName != "", + From: from, + To: to, + Limit: int64(limit), + TracesAttrsTable: tracesAttrsTable, + TracesAttrsDistTable: tracesAttrsDistTable, + TracesTable: tracesTable, + TracesDistTable: tracesDistTable, + Ctx: ctx, + CHDb: conn.Session, + CancelCtx: cancel, + VersionInfo: versionInfo, + }) + + if err != nil { + return nil, err + } + res := make(chan []model.TraceInfo) + go func() { + defer close(res) + defer cancel() + for ch := range ch { + res <- ch + } + }() + return res, nil +} diff --git a/reader/service/tempoService_test.go b/reader/service/tempoService_test.go new file mode 100644 index 00000000..9be437ba --- /dev/null +++ b/reader/service/tempoService_test.go @@ -0,0 +1,75 @@ +package service + +import ( + "fmt" + "testing" +) + +func TestOTLPToJSON(t *testing.T) { + str := `{ + "traceId": "BmnnjReqJcwSMLIHoMytSg==", + "spanId": "NywoCfe0bLc=", + "name": "test_span", + "kind": 1, + "startTimeUnixNano": "1734436231582466048", + "endTimeUnixNano": "1734436231683010560", + "attributes": [ + { + "key": "testId", + "value": { + "stringValue": "__TEST__" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "testSvc" + } + }, + { + "key": "telemetry.sdk.language", + "value": { + "stringValue": "nodejs" + } + }, + { + "key": "telemetry.sdk.name", + "value": { + "stringValue": "opentelemetry" + } + }, + { + "key": "telemetry.sdk.version", + "value": { + "stringValue": "0.25.0" + } + } + ], + "droppedAttributesCount": 0, + "events": [ + { + "timeUnixNano": "1734436231681999872", + "name": "test event", + "droppedAttributesCount": 0 + } + ], + "droppedEventsCount": 0, + "droppedLinksCount": 0, + "status": { + "code": 1 + } +}` + span, err := parseOTLPJson(&zipkinPayload{ + payload: str, + startTimeNs: 0, + durationNs: 0, + traceId: "", + spanId: "", + payloadType: 0, + parentId: "", + }) + if err != nil { + t.Error(err) + } + fmt.Println(span) +} diff --git a/reader/service/utils.go b/reader/service/utils.go new file mode 100644 index 00000000..fbb949f4 --- /dev/null +++ b/reader/service/utils.go @@ -0,0 +1,18 @@ +package service + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/clickhouse_planner" + "github.com/metrico/qryn/reader/model" + "time" +) + +func getTableName(ctx *model.DataDatabasesMap, name string) string { + if ctx.Config.ClusterName != "" { + return name + "_dist" + } + return name +} + +func FormatFromDate(from time.Time) string { + return clickhouse_planner.FormatFromDate(from) +} diff --git a/reader/system/system.go b/reader/system/system.go new file mode 100644 index 00000000..c9c0114d --- /dev/null +++ b/reader/system/system.go @@ -0,0 +1,14 @@ +package system + +// Code Action +// LOG_INFO +const ( + SYSLOG_LOG_EMERG = "LOG_EMERG" + SYSLOG_LOG_ALERT = "LOG_ALERT" + SYSLOG_LOG_CRIT = "LOG_CRIT" + SYSLOG_LOG_ERR = "LOG_ERR" + SYSLOG_LOG_WARNING = "LOG_WARNING" + SYSLOG_LOG_NOTICE = "LOG_NOTICE" + SYSLOG_LOG_INFO = "LOG_INFO" + SYSLOG_LOG_DEBUG = "LOG_DEBUG" +) diff --git a/reader/tempo/sqlIndexQuery.go b/reader/tempo/sqlIndexQuery.go new file mode 100644 index 00000000..fe5f45cd --- /dev/null +++ b/reader/tempo/sqlIndexQuery.go @@ -0,0 +1,154 @@ +package tempo + +import ( + "context" + "fmt" + "github.com/metrico/qryn/reader/utils/dbVersion" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "time" +) + +type SQLIndexQuery struct { + Tags string + FromNS int64 + ToNS int64 + MinDurationNS int64 + MaxDurationNS int64 + Limit int64 + Distributed bool + Database string + Ver dbVersion.VersionInfo + Ctx context.Context +} + +func (s *SQLIndexQuery) String(ctx *sql.Ctx, options ...int) (string, error) { + tableName := "`" + s.Database + "`.tempo_traces_attrs_gin" + if s.Distributed { + tableName += "_dist" + } + var ( + tags *Tags + err error + ) + if s.Tags != "" { + tags, err = tagsParser.ParseString("", s.Tags) + if err != nil { + return "", err + } + } + sqlTagRequests := make([]sql.ISelect, len(tags.Tags)) + for i, tag := range tags.Tags { + k, err := tag.Name.Parse() + if err != nil { + return "", err + } + v, err := tag.Val.Parse() + if err != nil { + return "", err + } + cond := opRegistry[tag.Condition] + if cond == nil { + return "", fmt.Errorf("no condition '%s'", tag.Condition) + } + sqlTagRequests[i] = sql.NewSelect(). + Select(sql.NewRawObject("trace_id"), sql.NewRawObject("span_id")). + From(sql.NewRawObject(tableName)). + AndWhere( + sql.Eq(sql.NewRawObject("key"), sql.NewStringVal(k)), + cond(sql.NewStringVal(v)), + //TODO: move to PRO !!!TURNED OFFF sql.Eq(sql.NewRawObject("oid"), sql.NewStringVal(s.Oid)), + ) + if s.Limit > 0 && s.Ver.IsVersionSupported("tempo_v2", s.FromNS, s.ToNS) { + sqlTagRequests[i].Select( + append(sqlTagRequests[i].GetSelect(), sql.NewRawObject("timestamp_ns"))...) + } + if s.FromNS > 0 { + from := time.Unix(s.FromNS/1e9, s.FromNS%1e9) + date := fmt.Sprintf("toDate('%s')", from.Format("2006-01-02")) + sqlTagRequests[i].AndWhere( + sql.Ge(sql.NewRawObject("date"), sql.NewRawObject(date)), + ) + if s.Ver.IsVersionSupported("tempo_v2", s.FromNS, s.ToNS) { + sqlTagRequests[i].AndWhere( + sql.Ge(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(s.FromNS))) + } + } + if s.ToNS > 0 { + to := time.Unix(s.ToNS/1e9, s.ToNS%1e9) + date := fmt.Sprintf("toDate('%s')", to.Format("2006-01-02")) + sqlTagRequests[i].AndWhere( + sql.Le(sql.NewRawObject("date"), sql.NewRawObject(date)), + ) + if s.Ver.IsVersionSupported("tempo_v2", s.FromNS, s.ToNS) { + sqlTagRequests[i].AndWhere( + sql.Le(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(s.ToNS))) + } + } + if s.MinDurationNS > 0 && s.Ver.IsVersionSupported("tempo_v2", s.FromNS, s.ToNS) { + sqlTagRequests[i].AndWhere( + sql.Ge(sql.NewRawObject("duration"), sql.NewIntVal(s.MinDurationNS))) + } + if s.MaxDurationNS > 0 && s.Ver.IsVersionSupported("tempo_v2", s.FromNS, s.ToNS) { + sqlTagRequests[i].AndWhere( + sql.Lt(sql.NewRawObject("duration"), sql.NewIntVal(s.MaxDurationNS))) + } + } + request := sql.NewSelect(). + Select(sql.NewRawObject("subsel_0.trace_id"), sql.NewRawObject("subsel_0.span_id")) + for i, subSel := range sqlTagRequests { + if i == 0 { + request.From(sql.NewCol(getSubSelect(subSel), "subsel_0")) + continue + } + alias := fmt.Sprintf("subsel_%d", i) + request.AddJoin(sql.NewJoin("INNER ANY", + sql.NewCol(getSubSelect(subSel), alias), + sql.And( + sql.Eq(sql.NewRawObject("subsel_0.trace_id"), sql.NewRawObject(alias+".trace_id")), + sql.Eq(sql.NewRawObject("subsel_0.span_id"), sql.NewRawObject(alias+".span_id")), + ), + )) + } + if s.Ver.IsVersionSupported("tempo_v2", s.FromNS, s.ToNS) && s.Limit > 0 { + request.OrderBy(sql.NewOrderBy(sql.NewRawObject("subsel_0.timestamp_ns"), sql.ORDER_BY_DIRECTION_DESC)). + Limit(sql.NewRawObject(fmt.Sprintf("%d", s.Limit))) + } + return request.String(ctx, options...) +} + +func getSubSelect(sel sql.SQLObject) sql.SQLObject { + return sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + str, err := sel.String(ctx, options...) + if err != nil { + return "", err + } + return fmt.Sprintf("(%s)", str), nil + }) +} + +var opRegistry = map[string]func(val sql.SQLObject) sql.SQLCondition{ + "=": func(val sql.SQLObject) sql.SQLCondition { + return sql.Eq(sql.NewRawObject("val"), val) + }, + "!=": func(val sql.SQLObject) sql.SQLCondition { + return sql.Neq(sql.NewRawObject("val"), val) + }, + "=~": func(val sql.SQLObject) sql.SQLCondition { + return sql.Eq(sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + strVal, err := val.String(ctx, options...) + if err != nil { + return "", err + } + return fmt.Sprintf("match(val, %s)", strVal), nil + }), sql.NewRawObject("1")) + }, + "!~": func(val sql.SQLObject) sql.SQLCondition { + return sql.Neq(sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { + strVal, err := val.String(ctx, options...) + if err != nil { + return "", err + } + return fmt.Sprintf("match(val, %s)", strVal), nil + }), sql.NewRawObject("1")) + }, +} diff --git a/reader/tempo/tags.go b/reader/tempo/tags.go new file mode 100644 index 00000000..e895a15e --- /dev/null +++ b/reader/tempo/tags.go @@ -0,0 +1,52 @@ +package tempo + +import ( + "github.com/alecthomas/participle/v2" + "github.com/alecthomas/participle/v2/lexer" + "strconv" +) + +var tagsLexer = lexer.MustStateful(lexer.Rules{ + "Root": { + {`OQuot`, `"`, lexer.Push("QString")}, + {`Literal`, `[^ !=~"]+`, nil}, + {`Cond`, `(!=|=~|!~|=)`, nil}, + {"space", `\s+`, nil}, + }, + "QString": { + {"Escaped", `\\.`, nil}, + {"Char", `[^"]`, nil}, + {"CQuot", `"`, lexer.Pop()}, + }, +}) + +type QuotedString struct { + Str string +} + +type LiteralOrQString struct { + Literal string `@Literal` + QString string `| (@OQuot(@Escaped|@Char)*@CQuot)` +} + +func (l LiteralOrQString) Parse() (string, error) { + if l.Literal != "" { + return l.Literal, nil + } + return strconv.Unquote(l.QString) +} + +type Tag struct { + Name LiteralOrQString `@@` + Condition string `@Cond` + Val LiteralOrQString `@@` +} + +type Tags struct { + Tags []Tag `@@*` +} + +var tagsParser = participle.MustBuild[Tags]( + participle.Lexer(tagsLexer), + participle.Elide("space"), +) diff --git a/reader/tempo/tracesQuery.go b/reader/tempo/tracesQuery.go new file mode 100644 index 00000000..7a053cd7 --- /dev/null +++ b/reader/tempo/tracesQuery.go @@ -0,0 +1,49 @@ +package tempo + +import ( + "context" + "github.com/metrico/qryn/reader/plugins" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "github.com/metrico/qryn/reader/utils/tables" +) + +func GetTracesQuery(ctx context.Context, idx *SQLIndexQuery, limit int, fromNS int64, toNS int64, + distributed bool, minDurationNS int64, maxDurationNS int64) (sql.ISelect, error) { + p := plugins.GetGetTracesQueryPlugin() + if p != nil { + return (*p)(ctx, idx, limit, fromNS, toNS, distributed, minDurationNS, maxDurationNS) + } + tableName := tables.GetTableName("tempo_traces") + if distributed { + tableName = tables.GetTableName("tempo_traces_dist") + } + query := sql.NewSelect().Select( + sql.NewRawObject("hex(trace_id)"), + sql.NewCol(sql.NewRawObject("service_name"), "root_service_name"), + sql.NewCol(sql.NewRawObject("name"), "root_trace_name"), + sql.NewCol(sql.NewRawObject("timestamp_ns"), "start_time_unix_nano"), + sql.NewCol(sql.NewRawObject("intDiv(duration_ns, 1000000)"), + "duration_ms"), + ).From(sql.NewRawObject(tableName)) + //TODO: move to PRO !TURNED OFF .AndWhere(sql.Eq(sql.NewRawObject("oid"), sql.NewStringVal(oid))) + if idx != nil { + query.AndWhere(sql.NewIn(sql.NewRawObject("(trace_id, span_id)"), idx)) + } + if fromNS > 0 { + query.AndWhere(sql.Gt(sql.NewRawObject("start_time_unix_nano"), sql.NewIntVal(fromNS))) + } + if toNS > 0 { + query.AndWhere(sql.Le(sql.NewRawObject("start_time_unix_nano"), sql.NewIntVal(toNS))) + } + if minDurationNS > 0 { + query.AndWhere(sql.Gt(sql.NewRawObject("duration_ms"), sql.NewIntVal(minDurationNS/1e6))) + } + if maxDurationNS > 0 { + query.AndWhere(sql.Le(sql.NewRawObject("duration_ms"), sql.NewIntVal(maxDurationNS/1e6))) + } + if limit > 0 { + query.Limit(sql.NewIntVal(int64(limit))) + } + query.OrderBy(sql.NewRawObject("start_time_unix_nano DESC")) + return query, nil +} diff --git a/wasm_parts/traceql/parser/lexer_rules v2.go b/reader/traceql/parser/lexer_rules v2.go similarity index 75% rename from wasm_parts/traceql/parser/lexer_rules v2.go rename to reader/traceql/parser/lexer_rules v2.go index 35e20911..da80950e 100644 --- a/wasm_parts/traceql/parser/lexer_rules v2.go +++ b/reader/traceql/parser/lexer_rules v2.go @@ -21,7 +21,7 @@ var TraceQLLexerRulesV2 = []lexer.SimpleRule{ {"Nre", `!~`}, {"Eq", `=`}, - {"Label_name", `(\.[a-zA-Z_][.a-zA-Z0-9_]*|[a-zA-Z_][.a-zA-Z0-9_]*)`}, + {"Label_name", `(\.[a-zA-Z_][.a-zA-Z0-9_-]*|[a-zA-Z_][.a-zA-Z0-9_-]*)`}, {"Dot", `\.`}, {"And", `&&`}, @@ -38,4 +38,4 @@ var TraceQLLexerRulesV2 = []lexer.SimpleRule{ {"space", `\s+`}, } -var TraceQLLexerDefinition = func() lexer.Definition { return lexer.MustSimple(TraceQLLexerRulesV2) } +var TraceQLLexerDefinition = lexer.MustSimple(TraceQLLexerRulesV2) diff --git a/wasm_parts/traceql/parser/model_v2.go b/reader/traceql/parser/model_v2.go similarity index 93% rename from wasm_parts/traceql/parser/model_v2.go rename to reader/traceql/parser/model_v2.go index d62deb83..95ac60ba 100644 --- a/wasm_parts/traceql/parser/model_v2.go +++ b/reader/traceql/parser/model_v2.go @@ -20,8 +20,8 @@ func (l TraceQLScript) String() string { } type Selector struct { - AttrSelector AttrSelectorExp `"{" @@ "}"` - Aggregator *Aggregator `@@?` + AttrSelector *AttrSelectorExp `"{" @@? "}"` + Aggregator *Aggregator `@@?` } func (s Selector) String() string { @@ -76,7 +76,7 @@ func (a AttrSelector) String() string { } type Value struct { - TimeVal string `@Integer @("ns"|"us"|"ms"|"s"|"m"|"h"|"d")` + TimeVal string `@Integer @Dot? @Integer? @("ns"|"us"|"ms"|"s"|"m"|"h"|"d")` FVal string `| @Minus? @Integer @Dot? @Integer?` StrVal *QuotedString `| @@` } diff --git a/wasm_parts/traceql/parser/parser.go b/reader/traceql/parser/parser.go similarity index 84% rename from wasm_parts/traceql/parser/parser.go rename to reader/traceql/parser/parser.go index e8c51d97..e3146f18 100644 --- a/wasm_parts/traceql/parser/parser.go +++ b/reader/traceql/parser/parser.go @@ -6,7 +6,7 @@ import ( func Parse(str string) (*TraceQLScript, error) { res := &TraceQLScript{} - parser, err := participle.Build[TraceQLScript](participle.Lexer(TraceQLLexerDefinition()), participle.UseLookahead(2)) + parser, err := participle.Build[TraceQLScript](participle.Lexer(TraceQLLexerDefinition), participle.UseLookahead(3)) if err != nil { return nil, err } diff --git a/wasm_parts/traceql/transpiler/clickhouse_transpiler/aggregator.go b/reader/traceql/transpiler/clickhouse_transpiler/aggregator.go similarity index 85% rename from wasm_parts/traceql/transpiler/clickhouse_transpiler/aggregator.go rename to reader/traceql/transpiler/clickhouse_transpiler/aggregator.go index 1ff7dac6..5cd7699d 100644 --- a/wasm_parts/traceql/transpiler/clickhouse_transpiler/aggregator.go +++ b/reader/traceql/transpiler/clickhouse_transpiler/aggregator.go @@ -1,10 +1,11 @@ package clickhouse_transpiler import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" "strconv" "time" - sql "wasm_parts/sql_select" - "wasm_parts/traceql/shared" ) type AggregatorPlanner struct { @@ -13,6 +14,7 @@ type AggregatorPlanner struct { Attr string CompareFn string CompareVal string + Prefix string fCmpVal float64 } @@ -59,7 +61,7 @@ func (a *AggregatorPlanner) cmpVal() error { func (a *AggregatorPlanner) getAggregator() (sql.SQLObject, error) { switch a.Fn { case "count": - return sql.NewRawObject("toFloat64(count(distinct index_search.span_id))"), nil + return sql.NewRawObject(fmt.Sprintf("toFloat64(count(distinct %sindex_search.span_id))", a.Prefix)), nil case "avg": return sql.NewRawObject("avgIf(agg_val, isNotNull(agg_val))"), nil case "max": diff --git a/reader/traceql/transpiler/clickhouse_transpiler/all_tags_request_planner.go b/reader/traceql/transpiler/clickhouse_transpiler/all_tags_request_planner.go new file mode 100644 index 00000000..ce7ea96b --- /dev/null +++ b/reader/traceql/transpiler/clickhouse_transpiler/all_tags_request_planner.go @@ -0,0 +1,20 @@ +package clickhouse_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/clickhouse_planner" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type AllTagsRequestPlanner struct { +} + +func (a *AllTagsRequestPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + return sql.NewSelect(). + Distinct(true). + Select(sql.NewSimpleCol("key", "key")). + From(sql.NewRawObject(ctx.TracesKVDistTable)). + AndWhere( + sql.Ge(sql.NewRawObject("date"), sql.NewStringVal(clickhouse_planner.FormatFromDate(ctx.From))), + sql.Le(sql.NewRawObject("date"), sql.NewStringVal(clickhouse_planner.FormatFromDate(ctx.To)))), nil +} diff --git a/reader/traceql/transpiler/clickhouse_transpiler/all_values_request_planner.go b/reader/traceql/transpiler/clickhouse_transpiler/all_values_request_planner.go new file mode 100644 index 00000000..7822a44a --- /dev/null +++ b/reader/traceql/transpiler/clickhouse_transpiler/all_values_request_planner.go @@ -0,0 +1,22 @@ +package clickhouse_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/clickhouse_planner" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type AllValuesRequestPlanner struct { + Key string +} + +func (a *AllValuesRequestPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + return sql.NewSelect(). + Distinct(true). + Select(sql.NewSimpleCol("val", "val")). + From(sql.NewRawObject(ctx.TracesKVDistTable)). + AndWhere( + sql.Ge(sql.NewRawObject("date"), sql.NewStringVal(clickhouse_planner.FormatFromDate(ctx.From))), + sql.Le(sql.NewRawObject("date"), sql.NewStringVal(clickhouse_planner.FormatFromDate(ctx.To))), + sql.Eq(sql.NewRawObject("key"), sql.NewStringVal(a.Key))), nil +} diff --git a/wasm_parts/traceql/transpiler/clickhouse_transpiler/attr_condition.go b/reader/traceql/transpiler/clickhouse_transpiler/attr_condition.go similarity index 86% rename from wasm_parts/traceql/transpiler/clickhouse_transpiler/attr_condition.go rename to reader/traceql/transpiler/clickhouse_transpiler/attr_condition.go index bc8ab898..6c4b399e 100644 --- a/wasm_parts/traceql/transpiler/clickhouse_transpiler/attr_condition.go +++ b/reader/traceql/transpiler/clickhouse_transpiler/attr_condition.go @@ -2,12 +2,12 @@ package clickhouse_transpiler import ( "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + traceql_parser "github.com/metrico/qryn/reader/traceql/parser" + sql "github.com/metrico/qryn/reader/utils/sql_select" "strconv" "strings" "time" - sql "wasm_parts/sql_select" - traceql_parser "wasm_parts/traceql/parser" - "wasm_parts/traceql/shared" ) type AttrConditionPlanner struct { @@ -29,10 +29,55 @@ func (a *AttrConditionPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, } a.alias = "bsCond" + err = a.maybeCreateWhere() + if err != nil { + return nil, err + } + + having, err := a.getCond(a.Conds) + if err != nil { + return nil, err + } + + err = a.aggregator(main) + if err != nil { + return nil, err + } + + res := main.AndWhere(sql.Or(a.where...)).AndHaving(having) + + if ctx.RandomFilter.Max != 0 && len(ctx.CachedTraceIds) > 0 { + rawCachedTraceIds := make([]sql.SQLObject, len(ctx.CachedTraceIds)) + for i, tid := range ctx.CachedTraceIds { + rawCachedTraceIds[i] = sql.NewRawObject(fmt.Sprintf("unhex('%s')", tid)) + } + res.AndWhere(sql.Or( + sql.Eq( + sql.NewRawObject(fmt.Sprintf("cityHash64(trace_id) %% %d", ctx.RandomFilter.Max)), + sql.NewIntVal(int64(ctx.RandomFilter.I)), + ), + sql.NewIn(sql.NewRawObject("trace_id"), rawCachedTraceIds...), + )) + } else if ctx.RandomFilter.Max != 0 { + res.AndWhere(sql.Eq( + sql.NewRawObject(fmt.Sprintf("cityHash64(trace_id) %% %d", ctx.RandomFilter.Max)), + sql.NewIntVal(int64(ctx.RandomFilter.I)), + )) + } + + a.isAliased = false + + return res, nil +} + +func (a *AttrConditionPlanner) maybeCreateWhere() error { + if len(a.sqlConds) > 0 { + return nil + } for _, t := range a.Terms { sqlTerm, err := a.getTerm(t) if err != nil { - return nil, err + return err } a.sqlConds = append(a.sqlConds, sqlTerm) @@ -44,18 +89,7 @@ func (a *AttrConditionPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, } a.where = append(a.where, sqlTerm) } - - having, err := a.getCond(a.Conds) - if err != nil { - return nil, err - } - - err = a.aggregator(main) - if err != nil { - return nil, err - } - - return main.AndWhere(sql.Or(a.where...)).AndHaving(having), nil + return nil } func (a *AttrConditionPlanner) aggregator(main sql.ISelect) error { diff --git a/reader/traceql/transpiler/clickhouse_transpiler/attr_condition_eval.go b/reader/traceql/transpiler/clickhouse_transpiler/attr_condition_eval.go new file mode 100644 index 00000000..ce5d448d --- /dev/null +++ b/reader/traceql/transpiler/clickhouse_transpiler/attr_condition_eval.go @@ -0,0 +1,47 @@ +package clickhouse_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +/* +const attrCondition = require('./attr_condition') +const {bitSet} = require('./shared') +const Sql = require('@cloki/clickhouse-sql') +module.exports = class Builder extends attrCondition { + build () { + const self = this + const superBuild = super.build() +const res = (ctx) => { +const sel = superBuild(ctx) +sel.having_conditions = [] +sel.aggregations = [bitSet(self.sqlConditions)] +sel.select_list = [[new Sql.Raw('count()'), 'count']] +sel.order_expressions = [] +return sel +} +return res +} +} +*/ + +type AttrConditionEvaluatorPlanner struct { + Main *AttrConditionPlanner + Prefix string +} + +func (a *AttrConditionEvaluatorPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := a.Main.Process(ctx) + if err != nil { + return nil, err + } + main.SetHaving(nil). + GroupBy(&bitSet{a.Main.sqlConds}, sql.NewRawObject("prefix")). + OrderBy(). + Select( + sql.NewCol(sql.NewStringVal(a.Prefix), "prefix"), + sql.NewSimpleCol("count()", "_count")) + + return main, nil +} diff --git a/reader/traceql/transpiler/clickhouse_transpiler/attrless.go b/reader/traceql/transpiler/clickhouse_transpiler/attrless.go new file mode 100644 index 00000000..5e417e98 --- /dev/null +++ b/reader/traceql/transpiler/clickhouse_transpiler/attrless.go @@ -0,0 +1,65 @@ +package clickhouse_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/plugins" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type AttrlessConditionPlanner struct { +} + +func NewAttrlessConditionPlanner() shared.SQLRequestPlanner { + p := plugins.GetAttrlessConditionPlannerPlugin() + if p != nil { + return (*p)() + } + return &AttrlessConditionPlanner{} +} + +func (a *AttrlessConditionPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + tracesTable := ctx.TracesTable + traceIds := sql.NewSelect().Select(sql.NewSimpleCol("trace_id", "trace_id")). + Distinct(true). + From(sql.NewSimpleCol(tracesTable, "traces")). + AndWhere(sql.And( + sql.Ge(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(ctx.From.UnixNano())), + sql.Le(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(ctx.To.UnixNano())), + )).OrderBy(sql.NewOrderBy(sql.NewRawObject("timestamp_ns"), sql.ORDER_BY_DIRECTION_DESC)). + Limit(sql.NewIntVal(ctx.Limit)) + withTraceIds := sql.NewWith(traceIds, "trace_ids") + traceAndSpanIds := sql.NewSelect(). + Select( + sql.NewSimpleCol("trace_id", "trace_id"), + sql.NewSimpleCol("groupArray(100)(span_id)", "span_id")). + From(sql.NewSimpleCol(tracesTable, "traces")). + AndWhere(sql.And( + sql.Ge(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(ctx.From.UnixNano())), + sql.Lt(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(ctx.To.UnixNano())), + sql.NewIn(sql.NewRawObject("trace_id"), sql.NewWithRef(withTraceIds)), + )). + GroupBy(sql.NewRawObject("trace_id")) + withTraceAndSpanIds := sql.NewWith(traceAndSpanIds, "trace_and_span_ids") + traceAndSpanIdsUnnested := sql.NewSelect(). + Select( + sql.NewSimpleCol("trace_id", "trace_id"), + sql.NewSimpleCol("_span_id", "span_id")). + From(sql.NewWithRef(withTraceAndSpanIds)). + Join(sql.NewJoin("array", sql.NewSimpleCol(withTraceAndSpanIds.GetAlias()+".span_id", "_span_id"), nil)) + withTraceAndSpanIdsUnnested := sql.NewWith(traceAndSpanIdsUnnested, "trace_and_span_ids_unnested") + return sql.NewSelect(). + With(withTraceIds, withTraceAndSpanIds, withTraceAndSpanIdsUnnested). + Select( + sql.NewSimpleCol("trace_id", "trace_id"), + sql.NewSimpleCol("span_id", "span_id"), + sql.NewSimpleCol("duration_ns", "duration"), + sql.NewSimpleCol("timestamp_ns", "timestamp_ns")). + From(sql.NewSimpleCol(tracesTable, "traces")). + AndWhere(sql.And( + sql.Ge(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(ctx.From.UnixNano())), + sql.Lt(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(ctx.To.UnixNano())), + sql.NewIn( + sql.NewRawObject("(traces.trace_id, traces.span_id)"), + sql.NewWithRef(withTraceAndSpanIdsUnnested)))). + OrderBy(sql.NewOrderBy(sql.NewRawObject("timestamp_ns"), sql.ORDER_BY_DIRECTION_DESC)), nil +} diff --git a/reader/traceql/transpiler/clickhouse_transpiler/attrless_eval.go b/reader/traceql/transpiler/clickhouse_transpiler/attrless_eval.go new file mode 100644 index 00000000..a09277e9 --- /dev/null +++ b/reader/traceql/transpiler/clickhouse_transpiler/attrless_eval.go @@ -0,0 +1,17 @@ +package clickhouse_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type AttrlessEvaluatorPlanner struct { + Prefix string +} + +func (a *AttrlessEvaluatorPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + return sql.NewSelect(). + Select( + sql.NewCol(sql.NewStringVal(a.Prefix), "prefix"), + sql.NewCol(sql.NewIntVal(ctx.Limit), "_count")), nil +} diff --git a/reader/traceql/transpiler/clickhouse_transpiler/complex_and.go b/reader/traceql/transpiler/clickhouse_transpiler/complex_and.go new file mode 100644 index 00000000..88e34fca --- /dev/null +++ b/reader/traceql/transpiler/clickhouse_transpiler/complex_and.go @@ -0,0 +1,67 @@ +package clickhouse_transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "strings" +) + +type ComplexAndPlanner struct { + Operands []shared.SQLRequestPlanner + Prefix string +} + +func (c ComplexAndPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + selects := make([]sql.ISelect, len(c.Operands)) + var err error + for i, op := range c.Operands { + selects[i], err = op.Process(ctx) + if err != nil { + return nil, err + } + selects[i].Select( + append(selects[i].GetSelect(), + sql.NewSimpleCol("max(timestamp_ns)", "max_timestamp_ns"))...) + with := sql.NewWith(selects[i], fmt.Sprintf("_%d_pre_", i)) + selects[i] = sql.NewSelect(). + With(with). + Select(sql.NewSimpleCol("trace_id", "trace_id"), + sql.NewSimpleCol("_span_id", "span_id"), + sql.NewSimpleCol("max_timestamp_ns", "max_timestamp_ns")). + From(sql.NewWithRef(with)). + Join(sql.NewJoin("array", sql.NewSimpleCol(with.GetAlias()+".span_id", "_span_id"), nil)) + } + + return sql.NewSelect(). + Select(sql.NewSimpleCol("trace_id", "trace_id"), + sql.NewSimpleCol("groupUniqArray(100)(span_id)", "span_id")). + From(sql.NewCol(&intersect{ + selects: selects, + }, c.Prefix+"a")). + GroupBy(sql.NewRawObject("trace_id")). + OrderBy(sql.NewOrderBy(sql.NewRawObject("max(max_timestamp_ns)"), sql.ORDER_BY_DIRECTION_DESC)), nil +} + +type intersect struct { + sql.ISelect + selects []sql.ISelect +} + +func (i *intersect) String(ctx *sql.Ctx, opts ...int) (string, error) { + var _opts []int + for _, opt := range opts { + if opt != sql.STRING_OPT_SKIP_WITH { + _opts = append(_opts, opt) + } + } + strSelects := make([]string, len(i.selects)) + var err error + for i, s := range i.selects { + strSelects[i], err = s.String(ctx, _opts...) + if err != nil { + return "", err + } + } + return fmt.Sprintf("(%s)", strings.Join(strSelects, " INTERSECT ")), nil +} diff --git a/reader/traceql/transpiler/clickhouse_transpiler/complex_eval_or.go b/reader/traceql/transpiler/clickhouse_transpiler/complex_eval_or.go new file mode 100644 index 00000000..2ef75dc5 --- /dev/null +++ b/reader/traceql/transpiler/clickhouse_transpiler/complex_eval_or.go @@ -0,0 +1,26 @@ +package clickhouse_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type ComplexEvalOrPlanner struct { + Operands []shared.SQLRequestPlanner + Prefix string +} + +func (c ComplexEvalOrPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + var err error + selects := make([]sql.ISelect, len(c.Operands)) + for i, op := range c.Operands { + selects[i], err = op.Process(ctx) + if err != nil { + return nil, err + } + } + res := sql.NewSelect(). + Select(sql.NewRawObject("*")). + From(sql.NewCol(&union{selects: selects}, c.Prefix+"a")) + return res, nil +} diff --git a/reader/traceql/transpiler/clickhouse_transpiler/complex_or.go b/reader/traceql/transpiler/clickhouse_transpiler/complex_or.go new file mode 100644 index 00000000..8cf90e08 --- /dev/null +++ b/reader/traceql/transpiler/clickhouse_transpiler/complex_or.go @@ -0,0 +1,66 @@ +package clickhouse_transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "strings" +) + +type ComplexOrPlanner struct { + Operands []shared.SQLRequestPlanner + Prefix string +} + +func (c ComplexOrPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + selects := make([]sql.ISelect, len(c.Operands)) + var err error + for i, op := range c.Operands { + selects[i], err = op.Process(ctx) + if err != nil { + return nil, err + } + selects[i].Select( + append(selects[i].GetSelect(), + sql.NewSimpleCol("max(timestamp_ns)", "max_timestamp_ns"))...) + with := sql.NewWith(selects[i], fmt.Sprintf("_%d_pre_", i)) + selects[i] = sql.NewSelect(). + With(with). + Select(sql.NewSimpleCol("trace_id", "trace_id"), + sql.NewSimpleCol("_span_id", "span_id"), + sql.NewSimpleCol("max_timestamp_ns", "max_timestamp_ns")). + From(sql.NewWithRef(with)). + Join(sql.NewJoin("array", sql.NewSimpleCol(with.GetAlias()+".span_id", "_span_id"), nil)) + } + + return sql.NewSelect(). + Select(sql.NewSimpleCol("trace_id", "trace_id"), + sql.NewSimpleCol("groupUniqArray(100)(span_id)", "span_id")). + From(sql.NewCol(&union{ + selects: selects, + }, c.Prefix+"a")). + GroupBy(sql.NewRawObject("trace_id")). + OrderBy(sql.NewOrderBy(sql.NewRawObject("max(max_timestamp_ns)"), sql.ORDER_BY_DIRECTION_DESC)), nil +} + +type union struct { + selects []sql.ISelect +} + +func (i *union) String(ctx *sql.Ctx, opts ...int) (string, error) { + var _opts []int + for _, opt := range opts { + if opt != sql.STRING_OPT_SKIP_WITH { + _opts = append(_opts, opt) + } + } + strSelects := make([]string, len(i.selects)) + var err error + for i, s := range i.selects { + strSelects[i], err = s.String(ctx, _opts...) + if err != nil { + return "", err + } + } + return fmt.Sprintf("(%s)", strings.Join(strSelects, " UNION ALL ")), nil +} diff --git a/reader/traceql/transpiler/clickhouse_transpiler/eval_finalizer.go b/reader/traceql/transpiler/clickhouse_transpiler/eval_finalizer.go new file mode 100644 index 00000000..a6cf88c4 --- /dev/null +++ b/reader/traceql/transpiler/clickhouse_transpiler/eval_finalizer.go @@ -0,0 +1,23 @@ +package clickhouse_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type EvalFinalizerPlanner struct { + Main shared.SQLRequestPlanner +} + +func (e *EvalFinalizerPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := e.Main.Process(ctx) + if err != nil { + return nil, err + } + + withPrefinal := sql.NewWith(main, "pre_final") + main = sql.NewSelect().With(withPrefinal). + Select(sql.NewSimpleCol("_count", "_count")). + From(sql.NewWithRef(withPrefinal)) + return main, nil +} diff --git a/reader/traceql/transpiler/clickhouse_transpiler/experssion_planner.go b/reader/traceql/transpiler/clickhouse_transpiler/experssion_planner.go new file mode 100644 index 00000000..e20f429b --- /dev/null +++ b/reader/traceql/transpiler/clickhouse_transpiler/experssion_planner.go @@ -0,0 +1,42 @@ +package clickhouse_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" +) + +type iExpressionPlanner interface { + fn() string + operands() []iExpressionPlanner + planner() (shared.SQLRequestPlanner, error) + addOp(selector iExpressionPlanner) + setOps(selector []iExpressionPlanner) + planEval() (shared.SQLRequestPlanner, error) +} + +type rootExpressionPlanner struct { + operand iExpressionPlanner +} + +func (r *rootExpressionPlanner) planEval() (shared.SQLRequestPlanner, error) { + return r.operand.planEval() +} + +func (r *rootExpressionPlanner) fn() string { + return "" +} + +func (r *rootExpressionPlanner) operands() []iExpressionPlanner { + return []iExpressionPlanner{r.operand} +} + +func (r *rootExpressionPlanner) planner() (shared.SQLRequestPlanner, error) { + return r.operand.planner() +} + +func (r *rootExpressionPlanner) addOp(selector iExpressionPlanner) { + r.operand = selector +} + +func (r *rootExpressionPlanner) setOps(selector []iExpressionPlanner) { + r.operand = selector[0] +} diff --git a/reader/traceql/transpiler/clickhouse_transpiler/expression_planner_complex.go b/reader/traceql/transpiler/clickhouse_transpiler/expression_planner_complex.go new file mode 100644 index 00000000..4641ab1e --- /dev/null +++ b/reader/traceql/transpiler/clickhouse_transpiler/expression_planner_complex.go @@ -0,0 +1,67 @@ +package clickhouse_transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" +) + +type complexExpressionPlanner struct { + prefix string + _fn string + _operands []iExpressionPlanner +} + +func (c *complexExpressionPlanner) planEval() (shared.SQLRequestPlanner, error) { + res := make([]shared.SQLRequestPlanner, len(c._operands)) + var err error + for i, operand := range c._operands { + res[i], err = operand.planEval() + if err != nil { + return nil, err + } + } + return &ComplexEvalOrPlanner{ + Operands: res, + Prefix: c.prefix, + }, nil +} + +func (c *complexExpressionPlanner) addOp(selector iExpressionPlanner) { + c._operands = append(c._operands, selector) +} + +func (c *complexExpressionPlanner) setOps(selector []iExpressionPlanner) { + c._operands = selector +} + +func (c *complexExpressionPlanner) fn() string { + return c._fn +} + +func (c *complexExpressionPlanner) operands() []iExpressionPlanner { + return c._operands +} + +func (c *complexExpressionPlanner) planner() (shared.SQLRequestPlanner, error) { + planners := make([]shared.SQLRequestPlanner, len(c._operands)) + var err error + for i, operand := range c._operands { + planners[i], err = operand.planner() + if err != nil { + return nil, err + } + } + switch c._fn { + case "||": + return &ComplexOrPlanner{ + Operands: planners, + Prefix: c.prefix, + }, nil + case "&&": + return &ComplexAndPlanner{ + Operands: planners, + Prefix: c.prefix, + }, nil + } + return nil, fmt.Errorf("unknown operator: %s", c._fn) +} diff --git a/reader/traceql/transpiler/clickhouse_transpiler/expression_planner_simple.go b/reader/traceql/transpiler/clickhouse_transpiler/expression_planner_simple.go new file mode 100644 index 00000000..6ee3542e --- /dev/null +++ b/reader/traceql/transpiler/clickhouse_transpiler/expression_planner_simple.go @@ -0,0 +1,209 @@ +package clickhouse_transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + traceql_parser "github.com/metrico/qryn/reader/traceql/parser" +) + +type simpleExpressionPlanner struct { + script *traceql_parser.TraceQLScript + prefix string + + //Analyze results + termIdx []*traceql_parser.AttrSelector + cond *condition + aggFn string + aggAttr string + cmpVal string + + terms map[string]int +} + +func (p *simpleExpressionPlanner) addOp(selector iExpressionPlanner) { +} + +func (p *simpleExpressionPlanner) setOps(selector []iExpressionPlanner) { +} + +func (p *simpleExpressionPlanner) fn() string { + return "" +} + +func (p *simpleExpressionPlanner) operands() []iExpressionPlanner { + return nil +} + +func (p *simpleExpressionPlanner) tagsV2Planner() (shared.SQLRequestPlanner, error) { + if p.script.Tail != nil { + return nil, fmt.Errorf("complex requests `{} || {} ...` are not supported") + } + err := p.check() + if err != nil { + return nil, err + } + + p.analyze() + + var res shared.SQLRequestPlanner = &AttrConditionPlanner{ + Main: NewInitIndexPlanner(false), + Terms: p.termIdx, + Conds: p.cond, + AggregatedAttr: p.aggAttr, + } + res = &SelectTagsPlanner{Main: res} + return res, nil +} + +func (p *simpleExpressionPlanner) valuesV2Planner(key string) (shared.SQLRequestPlanner, error) { + if p.script.Tail != nil { + return nil, fmt.Errorf("complex requests `{} || {} ...` are not supported") + } + err := p.check() + if err != nil { + return nil, err + } + + p.analyze() + + if p.cond == nil { + return &AllValuesRequestPlanner{Key: key}, nil + } + + var res shared.SQLRequestPlanner = &AttrConditionPlanner{ + Main: NewInitIndexPlanner(false), + Terms: p.termIdx, + Conds: p.cond, + AggregatedAttr: p.aggAttr, + } + res = &SelectValuesRequestPlanner{ + SelectTagsPlanner: SelectTagsPlanner{Main: res}, + Key: key, + } + return res, nil +} + +func (p *simpleExpressionPlanner) planner() (shared.SQLRequestPlanner, error) { + err := p.check() + if err != nil { + return nil, err + } + + p.analyze() + var res shared.SQLRequestPlanner + if p.script.Head.AttrSelector != nil { + res = &AttrConditionPlanner{ + Main: NewInitIndexPlanner(false), + Terms: p.termIdx, + Conds: p.cond, + AggregatedAttr: p.aggAttr, + } + } else { + res = NewAttrlessConditionPlanner() + } + + res = &IndexGroupByPlanner{Main: res, Prefix: p.prefix} + + if p.aggFn != "" { + res = &AggregatorPlanner{ + Main: res, + Fn: p.aggFn, + Attr: p.aggAttr, + CompareFn: p.script.Head.Aggregator.Cmp, + CompareVal: p.script.Head.Aggregator.Num + p.script.Head.Aggregator.Measurement, + Prefix: p.prefix, + } + } + return res, nil +} + +func (p *simpleExpressionPlanner) planEval() (shared.SQLRequestPlanner, error) { + err := p.check() + if err != nil { + return nil, err + } + + p.analyze() + var res shared.SQLRequestPlanner + if p.script.Head.AttrSelector != nil { + res = &AttrConditionEvaluatorPlanner{ + Main: &AttrConditionPlanner{ + Main: NewInitIndexPlanner(true), + Terms: p.termIdx, + Conds: p.cond, + AggregatedAttr: p.aggAttr, + }, + Prefix: p.prefix, + } + } else { + res = &AttrlessEvaluatorPlanner{ + Prefix: p.prefix, + } + } + + return res, nil +} + +func (p *simpleExpressionPlanner) check() error { + if p.script.Head.AttrSelector == nil { + if p.script.Head.Aggregator != nil { + return fmt.Errorf("requests like `{} | ....` are not supported") + } + if p.script.Tail != nil { + return fmt.Errorf("requests like `{} || .....` are not supported") + } + } + tail := p.script.Tail + for tail != nil { + if tail.Head.AttrSelector == nil { + return fmt.Errorf("requests like `... || {}` are not supported") + } + tail = tail.Tail + } + return nil +} + +func (p *simpleExpressionPlanner) analyze() { + p.terms = make(map[string]int) + p.cond = p.analyzeCond(p.script.Head.AttrSelector) + p.analyzeAgg() +} + +func (p *simpleExpressionPlanner) analyzeCond(exp *traceql_parser.AttrSelectorExp) *condition { + var res *condition + if exp == nil { + return nil + } + if exp.ComplexHead != nil { + res = p.analyzeCond(exp.ComplexHead) + } else if exp.Head != nil { + term := exp.Head.String() + if p.terms[term] != 0 { + res = &condition{simpleIdx: p.terms[term] - 1} + } else { + p.termIdx = append(p.termIdx, exp.Head) + p.terms[term] = len(p.termIdx) + res = &condition{simpleIdx: len(p.termIdx) - 1} + } + } + if exp.Tail != nil { + res = &condition{ + simpleIdx: -1, + op: exp.AndOr, + complex: []*condition{res, p.analyzeCond(exp.Tail)}, + } + } + return res +} + +func (p *simpleExpressionPlanner) analyzeAgg() { + if p.script.Head.Aggregator == nil { + return + } + + p.aggFn = p.script.Head.Aggregator.Fn + p.aggAttr = p.script.Head.Aggregator.Attr + + p.cmpVal = p.script.Head.Aggregator.Num + p.script.Head.Aggregator.Measurement + return +} diff --git a/wasm_parts/traceql/transpiler/clickhouse_transpiler/index_groupby.go b/reader/traceql/transpiler/clickhouse_transpiler/index_groupby.go similarity index 50% rename from wasm_parts/traceql/transpiler/clickhouse_transpiler/index_groupby.go rename to reader/traceql/transpiler/clickhouse_transpiler/index_groupby.go index 2eab14fe..126fcbd6 100644 --- a/wasm_parts/traceql/transpiler/clickhouse_transpiler/index_groupby.go +++ b/reader/traceql/transpiler/clickhouse_transpiler/index_groupby.go @@ -1,12 +1,14 @@ package clickhouse_transpiler import ( - sql "wasm_parts/sql_select" - "wasm_parts/traceql/shared" + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" ) type IndexGroupByPlanner struct { - Main shared.SQLRequestPlanner + Main shared.SQLRequestPlanner + Prefix string } func (i *IndexGroupByPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { @@ -15,17 +17,17 @@ func (i *IndexGroupByPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, return nil, err } - withMain := sql.NewWith(main, "index_search") + withMain := sql.NewWith(main, i.Prefix+"index_search") return sql.NewSelect(). With(withMain). Select( sql.NewSimpleCol("trace_id", "trace_id"), - sql.NewSimpleCol("groupArray(span_id)", "span_id"), - sql.NewSimpleCol("groupArray(duration)", "duration"), - sql.NewSimpleCol("groupArray(timestamp_ns)", "timestamp_ns")). + sql.NewSimpleCol("groupArray(100)(span_id)", "span_id")). From(sql.NewWithRef(withMain)). GroupBy(sql.NewRawObject("trace_id")). OrderBy( - sql.NewOrderBy(sql.NewRawObject("max(index_search.timestamp_ns)"), sql.ORDER_BY_DIRECTION_DESC), + sql.NewOrderBy(sql.NewRawObject( + fmt.Sprintf("max(%sindex_search.timestamp_ns)", i.Prefix)), + sql.ORDER_BY_DIRECTION_DESC), ), nil } diff --git a/wasm_parts/traceql/transpiler/clickhouse_transpiler/index_limit.go b/reader/traceql/transpiler/clickhouse_transpiler/index_limit.go similarity index 74% rename from wasm_parts/traceql/transpiler/clickhouse_transpiler/index_limit.go rename to reader/traceql/transpiler/clickhouse_transpiler/index_limit.go index 746a1231..a2c3ecab 100644 --- a/wasm_parts/traceql/transpiler/clickhouse_transpiler/index_limit.go +++ b/reader/traceql/transpiler/clickhouse_transpiler/index_limit.go @@ -1,8 +1,8 @@ package clickhouse_transpiler import ( - sql "wasm_parts/sql_select" - "wasm_parts/traceql/shared" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" ) type IndexLimitPlanner struct { diff --git a/wasm_parts/traceql/transpiler/clickhouse_transpiler/init.go b/reader/traceql/transpiler/clickhouse_transpiler/init.go similarity index 62% rename from wasm_parts/traceql/transpiler/clickhouse_transpiler/init.go rename to reader/traceql/transpiler/clickhouse_transpiler/init.go index abb08a82..f2303e30 100644 --- a/wasm_parts/traceql/transpiler/clickhouse_transpiler/init.go +++ b/reader/traceql/transpiler/clickhouse_transpiler/init.go @@ -1,22 +1,35 @@ package clickhouse_transpiler import ( - sql "wasm_parts/sql_select" - "wasm_parts/traceql/shared" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/plugins" + sql "github.com/metrico/qryn/reader/utils/sql_select" ) type InitIndexPlanner struct { + dist bool +} + +func NewInitIndexPlanner(dist bool) shared.SQLRequestPlanner { + p := plugins.GetInitIndexPlannerPlugin() + if p != nil { + return (*p)() + } + return &InitIndexPlanner{dist: dist} } func (i *InitIndexPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + table := ctx.TracesAttrsTable + if i.dist { + table = ctx.TracesAttrsDistTable + } return sql.NewSelect().Select( sql.NewSimpleCol("trace_id", "trace_id"), - sql.NewSimpleCol("lower(hex(span_id))", "span_id"), + sql.NewSimpleCol("span_id", "span_id"), sql.NewSimpleCol("any(duration)", "duration"), sql.NewSimpleCol("any(timestamp_ns)", "timestamp_ns")). - From(sql.NewSimpleCol(ctx.TracesAttrsTable, "traces_idx")). + From(sql.NewSimpleCol(table, "traces_idx")). AndWhere(sql.And( - sql.Eq(sql.NewRawObject("oid"), sql.NewStringVal(ctx.OrgID)), sql.Ge(sql.NewRawObject("date"), sql.NewStringVal(ctx.From.Format("2006-01-02"))), sql.Le(sql.NewRawObject("date"), sql.NewStringVal(ctx.To.Format("2006-01-02"))), sql.Ge(sql.NewRawObject("traces_idx.timestamp_ns"), sql.NewIntVal(ctx.From.UnixNano())), diff --git a/reader/traceql/transpiler/clickhouse_transpiler/planner.go b/reader/traceql/transpiler/clickhouse_transpiler/planner.go new file mode 100644 index 00000000..b2f30f25 --- /dev/null +++ b/reader/traceql/transpiler/clickhouse_transpiler/planner.go @@ -0,0 +1,130 @@ +package clickhouse_transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + traceql_parser "github.com/metrico/qryn/reader/traceql/parser" +) + +func Plan(script *traceql_parser.TraceQLScript) (shared.SQLRequestPlanner, error) { + return (&planner{script: script}).plan() +} + +func PlanEval(script *traceql_parser.TraceQLScript) (shared.SQLRequestPlanner, error) { + return (&planner{script: script}).planEval() +} + +func PlanTagsV2(script *traceql_parser.TraceQLScript) (shared.SQLRequestPlanner, error) { + return (&planner{script: script}).planTagsV2() +} + +func PlanValuesV2(script *traceql_parser.TraceQLScript, key string) (shared.SQLRequestPlanner, error) { + return (&planner{script: script}).planValuesV2(key) +} + +type planner struct { + script *traceql_parser.TraceQLScript + prefix int + + //Analyze results + termIdx []*traceql_parser.AttrSelector + cond *condition + aggFn string + aggAttr string + cmpVal string + + terms map[string]int +} + +func (p *planner) plan() (shared.SQLRequestPlanner, error) { + var res shared.SQLRequestPlanner + var err error + if p.script.Tail == nil { + res, err = (&simpleExpressionPlanner{script: p.script}).planner() + if err != nil { + return nil, err + } + } else { + root := &rootExpressionPlanner{} + p.planComplex(root, root, p.script) + res, err = root.planner() + if err != nil { + return nil, err + } + } + res = &IndexLimitPlanner{res} + + res = NewTracesDataPlanner(res) + + res = &IndexLimitPlanner{res} + + return res, nil +} + +func (p *planner) planTagsV2() (shared.SQLRequestPlanner, error) { + return (&simpleExpressionPlanner{script: p.script}).tagsV2Planner() +} + +func (p *planner) planValuesV2(key string) (shared.SQLRequestPlanner, error) { + return (&simpleExpressionPlanner{script: p.script}).valuesV2Planner(key) +} + +func (p *planner) getPrefix() string { + p.prefix++ + return fmt.Sprintf("_%d", p.prefix) +} + +func (p *planner) planComplex(root iExpressionPlanner, current iExpressionPlanner, + script *traceql_parser.TraceQLScript) { + switch script.AndOr { + case "": + current.addOp(&simpleExpressionPlanner{script: script, prefix: p.getPrefix()}) + case "&&": + current.addOp(&complexExpressionPlanner{ + prefix: p.getPrefix(), + _fn: "&&", + _operands: []iExpressionPlanner{&simpleExpressionPlanner{ + script: script, + prefix: p.getPrefix(), + }}, + }) + p.planComplex(root, current.operands()[0], script.Tail) + case "||": + current.addOp(&simpleExpressionPlanner{ + script: script, + prefix: p.getPrefix(), + }) + root.setOps([]iExpressionPlanner{&complexExpressionPlanner{ + prefix: p.getPrefix(), + _fn: "||", + _operands: root.operands(), + }}) + p.planComplex(root, root.operands()[0], script.Tail) + } +} + +func (p *planner) planEval() (shared.SQLRequestPlanner, error) { + var ( + res shared.SQLRequestPlanner + err error + ) + if p.script.Tail == nil { + res, err = (&simpleExpressionPlanner{script: p.script, prefix: p.getPrefix()}).planEval() + } else { + root := &rootExpressionPlanner{} + p.planComplex(root, root, p.script) + res, err = root.planEval() + } + if err != nil { + return nil, err + } + res = &EvalFinalizerPlanner{Main: res} + return res, nil +} + +type condition struct { + simpleIdx int // index of term; -1 means complex + + op string + complex []*condition +} diff --git a/reader/traceql/transpiler/clickhouse_transpiler/planner_test.go b/reader/traceql/transpiler/clickhouse_transpiler/planner_test.go new file mode 100644 index 00000000..0e3c03be --- /dev/null +++ b/reader/traceql/transpiler/clickhouse_transpiler/planner_test.go @@ -0,0 +1,83 @@ +package clickhouse_transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + traceql_parser "github.com/metrico/qryn/reader/traceql/parser" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "math/rand" + "testing" + "time" +) + +func TestPlanner(t *testing.T) { + script, err := traceql_parser.Parse(`{.randomContainer=~"admiring" && .randomFloat > 10}`) + if err != nil { + t.Fatal(err) + } + plan, err := Plan(script) + if err != nil { + t.Fatal(err) + } + + req, err := plan.Process(&shared.PlannerContext{ + IsCluster: false, + From: time.Now().Add(time.Hour * -44), + To: time.Now(), + Limit: 3, + TracesAttrsTable: "tempo_traces_attrs_gin", + TracesAttrsDistTable: "tempo_traces_attrs_gin_dist", + TracesTable: "tempo_traces", + TracesDistTable: "tempo_traces_dist", + VersionInfo: map[string]int64{}, + }) + if err != nil { + t.Fatal(err) + } + res, err := req.String(&sql.Ctx{ + Params: map[string]sql.SQLObject{}, + Result: map[string]sql.SQLObject{}, + }) + if err != nil { + t.Fatal(err) + } + fmt.Println(res) +} + +func TestComplexPlanner(t *testing.T) { + script, err := traceql_parser.Parse(`{.randomContainer=~"admiring" && .randomFloat > 10} | count() > 2 || {.randomContainer=~"boring" && .randomFloat < 10}`) + if err != nil { + t.Fatal(err) + } + plan, err := Plan(script) + if err != nil { + t.Fatal(err) + } + + req, err := plan.Process(&shared.PlannerContext{ + IsCluster: false, + From: time.Now().Add(time.Hour * -44), + To: time.Now(), + Limit: 3, + TracesAttrsTable: "tempo_traces_attrs_gin", + TracesAttrsDistTable: "tempo_traces_attrs_gin_dist", + TracesTable: "tempo_traces", + TracesDistTable: "tempo_traces_dist", + VersionInfo: map[string]int64{}, + }) + if err != nil { + t.Fatal(err) + } + res, err := req.String(&sql.Ctx{ + Params: map[string]sql.SQLObject{}, + Result: map[string]sql.SQLObject{}, + }) + if err != nil { + t.Fatal(err) + } + fmt.Println(res) +} + +func TestRandom(t *testing.T) { + fmt.Sprintf("%f", 50+(rand.Float64()*100-50)) +} diff --git a/reader/traceql/transpiler/clickhouse_transpiler/select_tags_planner.go b/reader/traceql/transpiler/clickhouse_transpiler/select_tags_planner.go new file mode 100644 index 00000000..1f33ae7e --- /dev/null +++ b/reader/traceql/transpiler/clickhouse_transpiler/select_tags_planner.go @@ -0,0 +1,48 @@ +package clickhouse_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type SelectTagsPlanner struct { + Main shared.SQLRequestPlanner +} + +func NewInitTagsPlanner() shared.SQLRequestPlanner { + //TODO: add this to plugins + /*p := plugins.GetInitIndexPlannerPlugin() + if p != nil { + return (*p)() + }*/ + return &SelectTagsPlanner{} +} + +func (i *SelectTagsPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := i.Main.Process(ctx) + if err != nil { + return nil, err + } + + withMain := sql.NewWith(main, "select_spans") + + preSelectTags := sql.NewSelect().Select(sql.NewRawObject("span_id")).From(sql.NewWithRef(withMain)) + withPreSelectTags := sql.NewWith(preSelectTags, "pre_select_tags") + + res := sql.NewSelect(). + With(withMain, withPreSelectTags). + Select(sql.NewSimpleCol("key", "key")). + From(sql.NewSimpleCol(ctx.TracesAttrsDistTable, "traces_idx")). + AndWhere(sql.And( + sql.Ge(sql.NewRawObject("date"), sql.NewStringVal(ctx.From.Format("2006-01-02"))), + sql.Le(sql.NewRawObject("date"), sql.NewStringVal(ctx.To.Format("2006-01-02"))), + sql.Ge(sql.NewRawObject("traces_idx.timestamp_ns"), sql.NewIntVal(ctx.From.UnixNano())), + sql.Lt(sql.NewRawObject("traces_idx.timestamp_ns"), sql.NewIntVal(ctx.To.UnixNano())), + sql.NewIn(sql.NewRawObject("span_id"), sql.NewWithRef(withPreSelectTags)), + )).GroupBy(sql.NewRawObject("trace_id"), sql.NewRawObject("span_id")) + if ctx.Limit > 0 { + res.OrderBy(sql.NewOrderBy(sql.NewRawObject("key"), sql.ORDER_BY_DIRECTION_ASC)). + Limit(sql.NewIntVal(ctx.Limit)) + } + return res, nil +} diff --git a/reader/traceql/transpiler/clickhouse_transpiler/select_values_planner.go b/reader/traceql/transpiler/clickhouse_transpiler/select_values_planner.go new file mode 100644 index 00000000..d04730a0 --- /dev/null +++ b/reader/traceql/transpiler/clickhouse_transpiler/select_values_planner.go @@ -0,0 +1,26 @@ +package clickhouse_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type SelectValuesRequestPlanner struct { + SelectTagsPlanner + Key string +} + +func (i *SelectValuesRequestPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := i.SelectTagsPlanner.Process(ctx) + if err != nil { + return nil, err + } + + main.Select(sql.NewSimpleCol("val", "val")). + AndWhere(sql.Eq(sql.NewRawObject("key"), sql.NewStringVal(i.Key))) + if ctx.Limit > 0 { + main.OrderBy(sql.NewOrderBy(sql.NewRawObject("val"), sql.ORDER_BY_DIRECTION_ASC)). + Limit(sql.NewIntVal(ctx.Limit)) + } + return main, nil +} diff --git a/wasm_parts/traceql/transpiler/clickhouse_transpiler/shared.go b/reader/traceql/transpiler/clickhouse_transpiler/shared.go similarity index 78% rename from wasm_parts/traceql/transpiler/clickhouse_transpiler/shared.go rename to reader/traceql/transpiler/clickhouse_transpiler/shared.go index 7bb1c911..6793305b 100644 --- a/wasm_parts/traceql/transpiler/clickhouse_transpiler/shared.go +++ b/reader/traceql/transpiler/clickhouse_transpiler/shared.go @@ -1,8 +1,8 @@ package clickhouse_transpiler import ( - sql "wasm_parts/sql_select" - "wasm_parts/traceql/shared" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" ) func getComparisonFn(op string) (func(left sql.SQLObject, right sql.SQLObject) *sql.LogicalOp, error) { diff --git a/reader/traceql/transpiler/clickhouse_transpiler/traces_data.go b/reader/traceql/transpiler/clickhouse_transpiler/traces_data.go new file mode 100644 index 00000000..1bc1eba2 --- /dev/null +++ b/reader/traceql/transpiler/clickhouse_transpiler/traces_data.go @@ -0,0 +1,79 @@ +package clickhouse_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/plugins" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type TracesDataPlanner struct { + Main shared.SQLRequestPlanner +} + +func NewTracesDataPlanner(main shared.SQLRequestPlanner) shared.SQLRequestPlanner { + p := plugins.GetTracesDataPlugin() + if p != nil { + return (*p)(main) + } + return &TracesDataPlanner{Main: main} +} + +func (t *TracesDataPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { + main, err := t.Main.Process(ctx) + if err != nil { + return nil, err + } + + table := ctx.TracesTable + if ctx.IsCluster { + table = ctx.TracesDistTable + } + + withMain := sql.NewWith(main, "index_grouped") + withTraceIds := sql.NewWith( + sql.NewSelect().Select(sql.NewRawObject("trace_id")).From(sql.NewWithRef(withMain)), + "trace_ids") + withTraceIdsRef := sql.NewWithRef(withTraceIds) + withTraceIdsSpanIds := sql.NewWith( + sql.NewSelect(). + Select(sql.NewRawObject("trace_id"), sql.NewRawObject("span_id")). + From(sql.NewWithRef(withMain)). + Join(sql.NewJoin("array", sql.NewRawObject("span_id"), nil)), + "trace_span_ids") + withTraceIdsSpanIdsRef := sql.NewWithRef(withTraceIdsSpanIds) + withTracesInfo := sql.NewWith( + sql.NewSelect(). + Select( + sql.NewSimpleCol("traces.trace_id", "trace_id"), + sql.NewSimpleCol("min(traces.timestamp_ns)", "_start_time_unix_nano"), + sql.NewSimpleCol("toFloat64(max(traces.timestamp_ns + traces.duration_ns) - min(traces.timestamp_ns)) / 1000000", "_duration_ms"), + sql.NewSimpleCol("argMin(traces.service_name, traces.timestamp_ns)", "_root_service_name"), + sql.NewSimpleCol("argMin(traces.name, traces.timestamp_ns)", "_root_trace_name")). + From(sql.NewSimpleCol(ctx.TracesTable, "traces")). + AndWhere(sql.NewIn(sql.NewRawObject("traces.trace_id"), withTraceIdsRef)). + GroupBy(sql.NewRawObject("traces.trace_id")), + "traces_info") + return sql.NewSelect(). + With(withMain, withTraceIds, withTraceIdsSpanIds, withTracesInfo). + Select( + sql.NewSimpleCol("lower(hex(traces.trace_id))", "trace_id"), + sql.NewSimpleCol(`arrayMap(x -> lower(hex(x)), groupArray(traces.span_id))`, "span_id"), + sql.NewSimpleCol(`groupArray(traces.duration_ns)`, "duration"), + sql.NewSimpleCol(`groupArray(traces.timestamp_ns)`, "timestamp_ns"), + sql.NewSimpleCol("min(_start_time_unix_nano)", "start_time_unix_nano"), + sql.NewSimpleCol("min(_duration_ms)", "duration_ms"), + sql.NewSimpleCol("min(_root_service_name)", "root_service_name"), + sql.NewSimpleCol("min(_root_trace_name)", "root_trace_name"), + ). + From(sql.NewSimpleCol(table, "traces")). + Join(sql.NewJoin( + "any left", + sql.NewWithRef(withTracesInfo), + sql.Eq(sql.NewRawObject("traces.trace_id"), sql.NewRawObject(withTracesInfo.GetAlias()+".trace_id"))), + ). + AndWhere( + sql.NewIn(sql.NewRawObject("traces.trace_id"), withTraceIdsRef), + sql.NewIn(sql.NewRawObject("(traces.trace_id, traces.span_id)"), withTraceIdsSpanIdsRef)). + GroupBy(sql.NewRawObject("traces.trace_id")). + OrderBy(sql.NewOrderBy(sql.NewRawObject("start_time_unix_nano"), sql.ORDER_BY_DIRECTION_DESC)), nil +} diff --git a/reader/traceql/transpiler/complex_request_processor.go b/reader/traceql/transpiler/complex_request_processor.go new file mode 100644 index 00000000..aad074b8 --- /dev/null +++ b/reader/traceql/transpiler/complex_request_processor.go @@ -0,0 +1,77 @@ +package traceql_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/model" + "strconv" + "time" +) + +type ComplexRequestProcessor struct { + main shared.SQLRequestPlanner +} + +func (t *ComplexRequestProcessor) Process(ctx *shared.PlannerContext, + complexity int64) (chan []model.TraceInfo, error) { + portions := (complexity + COMPLEXITY_THRESHOLD - 1) / COMPLEXITY_THRESHOLD + from := ctx.From + var cachedTraceIDs []string + var res []model.TraceInfo + for i := int64(0); i < portions; i++ { + ctx.RandomFilter = shared.RandomFilter{ + Max: int(portions), + I: int(i), + } + ctx.CachedTraceIds = cachedTraceIDs + ctx.From = from + var err error + res, from, cachedTraceIDs, err = t.ProcessComplexReqIteration(ctx) + if err != nil { + return nil, err + } + } + + for i := range res { + sortSpans(res[i].SpanSet.Spans) + } + + ch := make(chan []model.TraceInfo) + go func() { + defer close(ch) + ch <- res + }() + return ch, nil +} + +func (t *ComplexRequestProcessor) ProcessComplexReqIteration(ctx *shared.PlannerContext) ( + []model.TraceInfo, time.Time, []string, error) { + var res []model.TraceInfo + var from time.Time + var cachedTraceIDs []string + planner := &TraceQLRequestProcessor{t.main} + _res, err := planner.Process(ctx) + if err != nil { + return nil, from, cachedTraceIDs, err + } + for info := range _res { + for _, _info := range info { + startTimeUnixNano, err := strconv.ParseInt(_info.StartTimeUnixNano, 10, 64) + if err != nil { + return nil, from, cachedTraceIDs, err + } + if from.Nanosecond() == 0 || from.After(time.Unix(0, startTimeUnixNano)) { + from = time.Unix(0, startTimeUnixNano) + } + res = append(res, _info) + cachedTraceIDs = append(cachedTraceIDs, _info.TraceID) + } + } + if int64(len(res)) != ctx.Limit { + from = ctx.From + } + return res, from, cachedTraceIDs, nil +} + +func (c *ComplexRequestProcessor) SetMain(main shared.SQLRequestPlanner) { + c.main = main +} diff --git a/reader/traceql/transpiler/complex_tags_v2_processor.go b/reader/traceql/transpiler/complex_tags_v2_processor.go new file mode 100644 index 00000000..55374ad2 --- /dev/null +++ b/reader/traceql/transpiler/complex_tags_v2_processor.go @@ -0,0 +1,52 @@ +package traceql_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/traceql/transpiler/clickhouse_transpiler" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type allTagsV2RequestProcessor struct{} + +func (c *allTagsV2RequestProcessor) Process(ctx *shared.PlannerContext) (chan []string, error) { + planner := &clickhouse_transpiler.AllTagsRequestPlanner{} + req, err := planner.Process(ctx) + if err != nil { + return nil, err + } + + strReq, err := req.String(sql.DefaultCtx()) + if err != nil { + return nil, err + } + rows, err := ctx.CHDb.QueryCtx(ctx.Ctx, strReq) + if err != nil { + return nil, err + } + defer rows.Close() + var tags []string + for rows.Next() { + var tag string + err = rows.Scan(&tag) + if err != nil { + return nil, err + } + tags = append(tags, tag) + } + res := make(chan []string, 2) + res <- tags + go func() { close(res) }() + return res, nil +} + +type ComplexTagsV2RequestProcessor struct { + allTagsV2RequestProcessor +} + +func (c *ComplexTagsV2RequestProcessor) Process(ctx *shared.PlannerContext, + complexity int64) (chan []string, error) { + return c.allTagsV2RequestProcessor.Process(ctx) +} + +func (c *ComplexTagsV2RequestProcessor) SetMain(main shared.SQLRequestPlanner) { +} diff --git a/reader/traceql/transpiler/complex_values_v2_processor.go b/reader/traceql/transpiler/complex_values_v2_processor.go new file mode 100644 index 00000000..2f019590 --- /dev/null +++ b/reader/traceql/transpiler/complex_values_v2_processor.go @@ -0,0 +1,56 @@ +package traceql_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/traceql/transpiler/clickhouse_transpiler" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type allValuesV2RequestProcessor struct { + key string +} + +func (c *allValuesV2RequestProcessor) Process(ctx *shared.PlannerContext) (chan []string, error) { + planner := &clickhouse_transpiler.AllValuesRequestPlanner{ + Key: c.key, + } + req, err := planner.Process(ctx) + if err != nil { + return nil, err + } + + strReq, err := req.String(sql.DefaultCtx()) + if err != nil { + return nil, err + } + rows, err := ctx.CHDb.QueryCtx(ctx.Ctx, strReq) + if err != nil { + return nil, err + } + defer rows.Close() + var tags []string + for rows.Next() { + var tag string + err = rows.Scan(&tag) + if err != nil { + return nil, err + } + tags = append(tags, tag) + } + res := make(chan []string, 2) + res <- tags + go func() { close(res) }() + return res, nil +} + +type ComplexValuesV2RequestProcessor struct { + allValuesV2RequestProcessor +} + +func (c *ComplexValuesV2RequestProcessor) Process(ctx *shared.PlannerContext, + complexity int64) (chan []string, error) { + return c.allValuesV2RequestProcessor.Process(ctx) +} + +func (c *ComplexValuesV2RequestProcessor) SetMain(main shared.SQLRequestPlanner) { +} diff --git a/reader/traceql/transpiler/complexity_evaluator.go b/reader/traceql/transpiler/complexity_evaluator.go new file mode 100644 index 00000000..8e18b78b --- /dev/null +++ b/reader/traceql/transpiler/complexity_evaluator.go @@ -0,0 +1,85 @@ +package traceql_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/model" + sql "github.com/metrico/qryn/reader/utils/sql_select" + "sort" + "strconv" +) + +type simpleRequestProcessor[T any] interface { + Process(ctx *shared.PlannerContext) (chan []T, error) + SetMain(main shared.SQLRequestPlanner) +} + +type complexRequestProcessor[T any] interface { + Process(ctx *shared.PlannerContext, complexity int64) (chan []T, error) + SetMain(main shared.SQLRequestPlanner) +} + +type TraceQLComplexityEvaluator[T any] struct { + initSqlPlanner shared.SQLRequestPlanner + simpleRequestProcessor simpleRequestProcessor[T] + complexRequestProcessor complexRequestProcessor[T] + evaluateComplexityPlanner shared.SQLRequestPlanner +} + +const COMPLEXITY_THRESHOLD = 10000000 + +func (t *TraceQLComplexityEvaluator[T]) Process(ctx *shared.PlannerContext) (chan []T, error) { + evaluateComplexity, err := t.evaluateComplexityPlanner.Process(ctx) + if err != nil { + return nil, err + } + + sqlReq, err := evaluateComplexity.String(&sql.Ctx{ + Params: map[string]sql.SQLObject{}, + Result: map[string]sql.SQLObject{}, + }) + if err != nil { + return nil, err + } + + var complexity int64 + rows, err := ctx.CHDb.QueryCtx(ctx.Ctx, sqlReq) + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + var _complexity int64 + err = rows.Scan(&_complexity) + if err != nil { + return nil, err + } + + if _complexity > complexity { + complexity = _complexity + } + } + if complexity < COMPLEXITY_THRESHOLD { + return t.ProcessSimpleReq(ctx) + } + return t.ProcessComplexReq(ctx, complexity) +} + +func (t *TraceQLComplexityEvaluator[T]) ProcessSimpleReq(ctx *shared.PlannerContext) (chan []T, error) { + t.simpleRequestProcessor.SetMain(t.initSqlPlanner) + return t.simpleRequestProcessor.Process(ctx) +} + +func (t *TraceQLComplexityEvaluator[T]) ProcessComplexReq(ctx *shared.PlannerContext, + complexity int64) (chan []T, error) { + t.complexRequestProcessor.SetMain(t.initSqlPlanner) + return t.complexRequestProcessor.Process(ctx, complexity) +} + +func sortSpans(spans []model.SpanInfo) { + sort.Slice(spans, func(_i, j int) bool { + s1, _ := strconv.ParseInt(spans[_i].StartTimeUnixNano, 10, 64) + s2, _ := strconv.ParseInt(spans[j].StartTimeUnixNano, 10, 64) + return s1 > s2 + }) +} diff --git a/reader/traceql/transpiler/planner.go b/reader/traceql/transpiler/planner.go new file mode 100644 index 00000000..5b48d4f7 --- /dev/null +++ b/reader/traceql/transpiler/planner.go @@ -0,0 +1,71 @@ +package traceql_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/model" + traceql_parser "github.com/metrico/qryn/reader/traceql/parser" + "github.com/metrico/qryn/reader/traceql/transpiler/clickhouse_transpiler" +) + +func Plan(script *traceql_parser.TraceQLScript) (shared.TraceRequestProcessor, error) { + sqlPlanner, err := clickhouse_transpiler.Plan(script) + if err != nil { + return nil, err + } + + complexityPlanner, err := clickhouse_transpiler.PlanEval(script) + if err != nil { + return nil, err + } + + return &TraceQLComplexityEvaluator[model.TraceInfo]{ + initSqlPlanner: sqlPlanner, + simpleRequestProcessor: &SimpleRequestProcessor{}, + complexRequestProcessor: &ComplexRequestProcessor{}, + evaluateComplexityPlanner: complexityPlanner, + }, nil +} + +func PlanTagsV2(script *traceql_parser.TraceQLScript) (shared.GenericTraceRequestProcessor[string], error) { + if script == nil { + return &allTagsV2RequestProcessor{}, nil + } + res, err := clickhouse_transpiler.PlanTagsV2(script) + if err != nil { + return nil, err + } + + complexityPlanner, err := clickhouse_transpiler.PlanEval(script) + if err != nil { + return nil, err + } + + return &TraceQLComplexityEvaluator[string]{ + initSqlPlanner: res, + simpleRequestProcessor: &SimpleTagsV2RequestProcessor{}, + complexRequestProcessor: &ComplexTagsV2RequestProcessor{}, + evaluateComplexityPlanner: complexityPlanner, + }, nil +} + +func PlanValuesV2(script *traceql_parser.TraceQLScript, key string) (shared.GenericTraceRequestProcessor[string], error) { + if script == nil { + return &allTagsV2RequestProcessor{}, nil + } + res, err := clickhouse_transpiler.PlanValuesV2(script, key) + if err != nil { + return nil, err + } + + complexityPlanner, err := clickhouse_transpiler.PlanEval(script) + if err != nil { + return nil, err + } + + return &TraceQLComplexityEvaluator[string]{ + initSqlPlanner: res, + simpleRequestProcessor: &SimpleTagsV2RequestProcessor{}, + complexRequestProcessor: &ComplexValuesV2RequestProcessor{}, + evaluateComplexityPlanner: complexityPlanner, + }, nil +} diff --git a/reader/traceql/transpiler/reqest_processor.go b/reader/traceql/transpiler/reqest_processor.go new file mode 100644 index 00000000..23a1f269 --- /dev/null +++ b/reader/traceql/transpiler/reqest_processor.go @@ -0,0 +1,91 @@ +package traceql_transpiler + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/utils/logger" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type TraceQLRequestProcessor struct { + sqlPlanner shared.SQLRequestPlanner +} + +func (t TraceQLRequestProcessor) Process(ctx *shared.PlannerContext) (chan []model.TraceInfo, error) { + sqlReq, err := t.sqlPlanner.Process(ctx) + if err != nil { + return nil, err + } + + var opts []int + if ctx.IsCluster { + opts = append(opts, sql.STRING_OPT_INLINE_WITH) + } + + strReq, err := sqlReq.String(&sql.Ctx{ + Params: map[string]sql.SQLObject{}, + Result: map[string]sql.SQLObject{}, + }) + + rows, err := ctx.CHDb.QueryCtx(ctx.Ctx, strReq) + if err != nil { + return nil, err + } + + res := make(chan []model.TraceInfo) + + go func() { + defer rows.Close() + defer close(res) + + for rows.Next() { + var ( + traceId string + spanIds []string + durationsNs []int64 + timestampsNs []int64 + startTimeUnixNano int64 + traceDurationMs float64 + rootServiceName string + rootTraceName string + ) + err = rows.Scan(&traceId, &spanIds, &durationsNs, ×tampsNs, + &startTimeUnixNano, &traceDurationMs, &rootServiceName, &rootTraceName) + if err != nil { + logger.Error("ERROR[TRP#1]: ", err) + return + } + for i := range durationsNs { + if durationsNs[i] == timestampsNs[i] { + durationsNs[i] = -1 + } + } + trace := model.TraceInfo{ + TraceID: traceId, + RootServiceName: rootServiceName, + RootTraceName: rootTraceName, + StartTimeUnixNano: fmt.Sprintf("%d", startTimeUnixNano), + DurationMs: traceDurationMs, + SpanSet: model.SpanSet{ + Spans: make([]model.SpanInfo, len(spanIds)), + }, + } + for i, spanId := range spanIds { + trace.SpanSet.Spans[i].SpanID = spanId + trace.SpanSet.Spans[i].DurationNanos = fmt.Sprintf("%d", durationsNs[i]) + if durationsNs[i] == -1 { + trace.SpanSet.Spans[i].DurationNanos = "n/a" + } + trace.SpanSet.Spans[i].StartTimeUnixNano = fmt.Sprintf("%d", timestampsNs[i]) + trace.SpanSet.Spans[i].Attributes = make([]model.SpanAttr, 0) + } + trace.SpanSet.Matched = len(trace.SpanSet.Spans) + trace.SpanSets = []model.SpanSet{trace.SpanSet} + sortSpans(trace.SpanSet.Spans) + res <- []model.TraceInfo{trace} + } + }() + + return res, nil +} diff --git a/reader/traceql/transpiler/simple_request_processor.go b/reader/traceql/transpiler/simple_request_processor.go new file mode 100644 index 00000000..be466fda --- /dev/null +++ b/reader/traceql/transpiler/simple_request_processor.go @@ -0,0 +1,19 @@ +package traceql_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/model" +) + +type SimpleRequestProcessor struct { + main shared.SQLRequestPlanner +} + +func (s *SimpleRequestProcessor) Process(ctx *shared.PlannerContext) (chan []model.TraceInfo, error) { + planner := &TraceQLRequestProcessor{s.main} + return planner.Process(ctx) +} + +func (s *SimpleRequestProcessor) SetMain(main shared.SQLRequestPlanner) { + s.main = main +} diff --git a/reader/traceql/transpiler/simple_tags_v2_processor.go b/reader/traceql/transpiler/simple_tags_v2_processor.go new file mode 100644 index 00000000..d50d2178 --- /dev/null +++ b/reader/traceql/transpiler/simple_tags_v2_processor.go @@ -0,0 +1,48 @@ +package traceql_transpiler + +import ( + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + sql "github.com/metrico/qryn/reader/utils/sql_select" +) + +type SimpleTagsV2RequestProcessor struct { + main shared.SQLRequestPlanner +} + +func (s *SimpleTagsV2RequestProcessor) Process(ctx *shared.PlannerContext) (chan []string, error) { + req, err := s.main.Process(ctx) + if err != nil { + return nil, err + } + + strReq, err := req.String(sql.DefaultCtx()) + if err != nil { + return nil, err + } + println(strReq) + + rows, err := ctx.CHDb.QueryCtx(ctx.Ctx, strReq) + if err != nil { + return nil, err + } + defer rows.Close() + + var res []string + for rows.Next() { + var tag string + err = rows.Scan(&tag) + if err != nil { + return nil, err + } + res = append(res, tag) + } + + cRes := make(chan []string, 2) + cRes <- res + go func() { close(cRes) }() + return cRes, nil +} + +func (s *SimpleTagsV2RequestProcessor) SetMain(main shared.SQLRequestPlanner) { + s.main = main +} diff --git a/reader/utils/cityhash102/cityhash.go b/reader/utils/cityhash102/cityhash.go new file mode 100644 index 00000000..dcc666b6 --- /dev/null +++ b/reader/utils/cityhash102/cityhash.go @@ -0,0 +1,383 @@ +/* + * Go implementation of Google city hash (MIT license) + * https://code.google.com/p/cityhash/ + * + * MIT License http://www.opensource.org/licenses/mit-license.php + * + * I don't even want to pretend to understand the details of city hash. + * I am only reproducing the logic in Go as faithfully as I can. + * + */ + +package cityhash102 + +import ( + "encoding/binary" +) + +const ( + k0 uint64 = 0xc3a5c85c97cb3127 + k1 uint64 = 0xb492b66fbe98f273 + k2 uint64 = 0x9ae16a3b2f90404f + k3 uint64 = 0xc949d7c7509e6557 + + kMul uint64 = 0x9ddfea08eb382d69 +) + +func fetch64(p []byte) uint64 { + return binary.LittleEndian.Uint64(p) + //return uint64InExpectedOrder(unalignedLoad64(p)) +} + +func fetch32(p []byte) uint32 { + return binary.LittleEndian.Uint32(p) + //return uint32InExpectedOrder(unalignedLoad32(p)) +} + +func rotate64(val uint64, shift uint32) uint64 { + if shift != 0 { + return ((val >> shift) | (val << (64 - shift))) + } + + return val +} + +func rotate32(val uint32, shift uint32) uint32 { + if shift != 0 { + return ((val >> shift) | (val << (32 - shift))) + } + + return val +} + +func swap64(a, b *uint64) { + *a, *b = *b, *a +} + +func swap32(a, b *uint32) { + *a, *b = *b, *a +} + +func permute3(a, b, c *uint32) { + swap32(a, b) + swap32(a, c) +} + +func rotate64ByAtLeast1(val uint64, shift uint32) uint64 { + return (val >> shift) | (val << (64 - shift)) +} + +func shiftMix(val uint64) uint64 { + return val ^ (val >> 47) +} + +type Uint128 [2]uint64 + +func (this *Uint128) setLower64(l uint64) { + this[0] = l +} + +func (this *Uint128) setHigher64(h uint64) { + this[1] = h +} + +func (this Uint128) Lower64() uint64 { + return this[0] +} + +func (this Uint128) Higher64() uint64 { + return this[1] +} + +func (this Uint128) Bytes() []byte { + b := make([]byte, 16) + binary.LittleEndian.PutUint64(b, this[0]) + binary.LittleEndian.PutUint64(b[8:], this[1]) + return b +} + +func hash128to64(x Uint128) uint64 { + // Murmur-inspired hashing. + var a = (x.Lower64() ^ x.Higher64()) * kMul + a ^= (a >> 47) + var b = (x.Higher64() ^ a) * kMul + b ^= (b >> 47) + b *= kMul + return b +} + +func hashLen16(u, v uint64) uint64 { + return hash128to64(Uint128{u, v}) +} + +func hashLen16_3(u, v, mul uint64) uint64 { + // Murmur-inspired hashing. + var a = (u ^ v) * mul + a ^= (a >> 47) + var b = (v ^ a) * mul + b ^= (b >> 47) + b *= mul + return b +} + +func hashLen0to16(s []byte, length uint32) uint64 { + if length > 8 { + var a = fetch64(s) + var b = fetch64(s[length-8:]) + + return hashLen16(a, rotate64ByAtLeast1(b+uint64(length), length)) ^ b + } + + if length >= 4 { + var a = fetch32(s) + return hashLen16(uint64(length)+(uint64(a)<<3), uint64(fetch32(s[length-4:]))) + } + + if length > 0 { + var a uint8 = uint8(s[0]) + var b uint8 = uint8(s[length>>1]) + var c uint8 = uint8(s[length-1]) + + var y uint32 = uint32(a) + (uint32(b) << 8) + var z uint32 = length + (uint32(c) << 2) + + return shiftMix(uint64(y)*k2^uint64(z)*k3) * k2 + } + + return k2 +} + +// This probably works well for 16-byte strings as well, but it may be overkill +func hashLen17to32(s []byte, length uint32) uint64 { + var a = fetch64(s) * k1 + var b = fetch64(s[8:]) + var c = fetch64(s[length-8:]) * k2 + var d = fetch64(s[length-16:]) * k0 + + return hashLen16(rotate64(a-b, 43)+rotate64(c, 30)+d, + a+rotate64(b^k3, 20)-c+uint64(length)) +} + +func weakHashLen32WithSeeds(w, x, y, z, a, b uint64) Uint128 { + a += w + b = rotate64(b+a+z, 21) + var c uint64 = a + a += x + a += y + b += rotate64(a, 44) + return Uint128{a + z, b + c} +} + +func weakHashLen32WithSeeds_3(s []byte, a, b uint64) Uint128 { + return weakHashLen32WithSeeds(fetch64(s), fetch64(s[8:]), fetch64(s[16:]), fetch64(s[24:]), a, b) +} + +func hashLen33to64(s []byte, length uint32) uint64 { + var z uint64 = fetch64(s[24:]) + var a uint64 = fetch64(s) + (uint64(length)+fetch64(s[length-16:]))*k0 + var b uint64 = rotate64(a+z, 52) + var c uint64 = rotate64(a, 37) + + a += fetch64(s[8:]) + c += rotate64(a, 7) + a += fetch64(s[16:]) + + var vf uint64 = a + z + var vs = b + rotate64(a, 31) + c + + a = fetch64(s[16:]) + fetch64(s[length-32:]) + z = fetch64(s[length-8:]) + b = rotate64(a+z, 52) + c = rotate64(a, 37) + a += fetch64(s[length-24:]) + c += rotate64(a, 7) + a += fetch64(s[length-16:]) + + wf := a + z + ws := b + rotate64(a, 31) + c + r := shiftMix((vf+ws)*k2 + (wf+vs)*k0) + return shiftMix(r*k0+vs) * k2 +} + +func CityHash64(s []byte, length uint32) uint64 { + if length <= 32 { + if length <= 16 { + return hashLen0to16(s, length) + } else { + return hashLen17to32(s, length) + } + } else if length <= 64 { + return hashLen33to64(s, length) + } + + var x uint64 = fetch64(s) + var y uint64 = fetch64(s[length-16:]) ^ k1 + var z uint64 = fetch64(s[length-56:]) ^ k0 + + var v Uint128 = weakHashLen32WithSeeds_3(s[length-64:], uint64(length), y) + var w Uint128 = weakHashLen32WithSeeds_3(s[length-32:], uint64(length)*k1, k0) + + z += shiftMix(v.Higher64()) * k1 + x = rotate64(z+x, 39) * k1 + y = rotate64(y, 33) * k1 + + length = (length - 1) & ^uint32(63) + for { + x = rotate64(x+y+v.Lower64()+fetch64(s[16:]), 37) * k1 + y = rotate64(y+v.Higher64()+fetch64(s[48:]), 42) * k1 + + x ^= w.Higher64() + y ^= v.Lower64() + + z = rotate64(z^w.Lower64(), 33) + v = weakHashLen32WithSeeds_3(s, v.Higher64()*k1, x+w.Lower64()) + w = weakHashLen32WithSeeds_3(s[32:], z+w.Higher64(), y) + + swap64(&z, &x) + s = s[64:] + length -= 64 + + if length == 0 { + break + } + } + + return hashLen16(hashLen16(v.Lower64(), w.Lower64())+shiftMix(y)*k1+z, hashLen16(v.Higher64(), w.Higher64())+x) +} + +func CityHash64WithSeed(s []byte, length uint32, seed uint64) uint64 { + return CityHash64WithSeeds(s, length, k2, seed) +} + +func CityHash64WithSeeds(s []byte, length uint32, seed0, seed1 uint64) uint64 { + return hashLen16(CityHash64(s, length)-seed0, seed1) +} + +func cityMurmur(s []byte, length uint32, seed Uint128) Uint128 { + var a uint64 = seed.Lower64() + var b uint64 = seed.Higher64() + var c uint64 = 0 + var d uint64 = 0 + var l int32 = int32(length) - 16 + + if l <= 0 { // len <= 16 + a = shiftMix(a*k1) * k1 + c = b*k1 + hashLen0to16(s, length) + + if length >= 8 { + d = shiftMix(a + fetch64(s)) + } else { + d = shiftMix(a + c) + } + + } else { // len > 16 + c = hashLen16(fetch64(s[length-8:])+k1, a) + d = hashLen16(b+uint64(length), c+fetch64(s[length-16:])) + a += d + + for { + a ^= shiftMix(fetch64(s)*k1) * k1 + a *= k1 + b ^= a + c ^= shiftMix(fetch64(s[8:])*k1) * k1 + c *= k1 + d ^= c + s = s[16:] + l -= 16 + + if l <= 0 { + break + } + } + } + a = hashLen16(a, c) + b = hashLen16(d, b) + return Uint128{a ^ b, hashLen16(b, a)} +} + +func CityHash128WithSeed(s []byte, length uint32, seed Uint128) Uint128 { + if length < 128 { + return cityMurmur(s, length, seed) + } + + // We expect length >= 128 to be the common case. Keep 56 bytes of state: + // v, w, x, y, and z. + var v, w Uint128 + var x uint64 = seed.Lower64() + var y uint64 = seed.Higher64() + var z uint64 = uint64(length) * k1 + + var pos uint32 + var t = s + + v.setLower64(rotate64(y^k1, 49)*k1 + fetch64(s)) + v.setHigher64(rotate64(v.Lower64(), 42)*k1 + fetch64(s[8:])) + w.setLower64(rotate64(y+z, 35)*k1 + x) + w.setHigher64(rotate64(x+fetch64(s[88:]), 53) * k1) + + // This is the same inner loop as CityHash64(), manually unrolled. + for { + x = rotate64(x+y+v.Lower64()+fetch64(s[16:]), 37) * k1 + y = rotate64(y+v.Higher64()+fetch64(s[48:]), 42) * k1 + + x ^= w.Higher64() + y ^= v.Lower64() + z = rotate64(z^w.Lower64(), 33) + v = weakHashLen32WithSeeds_3(s, v.Higher64()*k1, x+w.Lower64()) + w = weakHashLen32WithSeeds_3(s[32:], z+w.Higher64(), y) + swap64(&z, &x) + s = s[64:] + pos += 64 + + x = rotate64(x+y+v.Lower64()+fetch64(s[16:]), 37) * k1 + y = rotate64(y+v.Higher64()+fetch64(s[48:]), 42) * k1 + x ^= w.Higher64() + y ^= v.Lower64() + z = rotate64(z^w.Lower64(), 33) + v = weakHashLen32WithSeeds_3(s, v.Higher64()*k1, x+w.Lower64()) + w = weakHashLen32WithSeeds_3(s[32:], z+w.Higher64(), y) + swap64(&z, &x) + s = s[64:] + pos += 64 + length -= 128 + + if length < 128 { + break + } + } + + y += rotate64(w.Lower64(), 37)*k0 + z + x += rotate64(v.Lower64()+z, 49) * k0 + + // If 0 < length < 128, hash up to 4 chunks of 32 bytes each from the end of s. + var tailDone uint32 + for tailDone = 0; tailDone < length; { + tailDone += 32 + y = rotate64(y-x, 42)*k0 + v.Higher64() + + //TODO why not use origin_len ? + w.setLower64(w.Lower64() + fetch64(t[pos+length-tailDone+16:])) + x = rotate64(x, 49)*k0 + w.Lower64() + w.setLower64(w.Lower64() + v.Lower64()) + v = weakHashLen32WithSeeds_3(t[pos+length-tailDone:], v.Lower64(), v.Higher64()) + } + // At this point our 48 bytes of state should contain more than + // enough information for a strong 128-bit hash. We use two + // different 48-byte-to-8-byte hashes to get a 16-byte final result. + x = hashLen16(x, v.Lower64()) + y = hashLen16(y, w.Lower64()) + + return Uint128{hashLen16(x+v.Higher64(), w.Higher64()) + y, + hashLen16(x+w.Higher64(), y+v.Higher64())} +} + +func CityHash128(s []byte, length uint32) (result Uint128) { + if length >= 16 { + result = CityHash128WithSeed(s[16:length], length-16, Uint128{fetch64(s) ^ k3, fetch64(s[8:])}) + } else if length >= 8 { + result = CityHash128WithSeed(nil, 0, Uint128{fetch64(s) ^ (uint64(length) * k0), fetch64(s[length-8:]) ^ k1}) + } else { + result = CityHash128WithSeed(s, length, Uint128{k0, k1}) + } + return +} diff --git a/reader/utils/dbVersion/version.go b/reader/utils/dbVersion/version.go new file mode 100644 index 00000000..3ff16790 --- /dev/null +++ b/reader/utils/dbVersion/version.go @@ -0,0 +1,92 @@ +package dbVersion + +import ( + "context" + "fmt" + "github.com/metrico/qryn/reader/model" + "strconv" + "sync" + "sync/atomic" + "time" +) + +type VersionInfo map[string]int64 + +func (v VersionInfo) IsVersionSupported(ver string, fromNS int64, toNS int64) bool { + time, ok := v[ver] + fmt.Printf("Checking %d - %d", fromNS, time) + return ok && (fromNS >= (time * 1000000000)) +} + +var versions = make(map[string]VersionInfo, 10) +var mtx sync.Mutex +var throttled int32 = 0 + +func throttle() { + if !atomic.CompareAndSwapInt32(&throttled, 0, 1) { + return + } + go func() { + time.Sleep(time.Second * 10) + atomic.StoreInt32(&throttled, 0) + mtx.Lock() + versions = make(map[string]VersionInfo, 10) + mtx.Unlock() + }() +} + +func GetVersionInfo(ctx context.Context, dist bool, db model.ISqlxDB) (VersionInfo, error) { + mtx.Lock() + ver, ok := versions[db.GetName()] + mtx.Unlock() + if ok { + return ver, nil + } + tableName := "settings" + if dist { + tableName += "_dist" + } + _versions := map[string]int64{} + rows, err := db.QueryCtx(ctx, fmt.Sprintf(`SELECT argMax(name, inserted_at) as _name , argMax(value, inserted_at) as _value +FROM %s WHERE type='update' GROUP BY fingerprint HAVING _name!=''`, tableName)) + if err != nil { + return nil, err + } + defer rows.Close() + for rows.Next() { + var ver, time string + err = rows.Scan(&ver, &time) + if err != nil { + fmt.Println(err) + continue + } + _time, err := strconv.ParseInt(time, 10, 64) + if err == nil { + _versions[ver] = _time + } + } + + tables, err := db.QueryCtx(ctx, fmt.Sprintf(`SHOW TABLES`)) + if err != nil { + return nil, err + } + defer tables.Close() + metrics15sV1 := false + for tables.Next() { + var tableName string + err = tables.Scan(&tableName) + if err != nil { + fmt.Println(err) + continue + } + metrics15sV1 = metrics15sV1 || tableName == "metrics_15s" || tableName == "metrics_15s_dist" + } + if !metrics15sV1 { + _versions["v5"] = 0 + } + mtx.Lock() + versions[db.GetName()] = _versions + mtx.Unlock() + throttle() + return _versions, nil +} diff --git a/reader/utils/dsn/sqlxWrap.go b/reader/utils/dsn/sqlxWrap.go new file mode 100644 index 00000000..08843082 --- /dev/null +++ b/reader/utils/dsn/sqlxWrap.go @@ -0,0 +1,88 @@ +package dsn + +import ( + "context" + "database/sql" + "fmt" + "github.com/jmoiron/sqlx" + "sync" +) + +type StableSqlxDBWrapper struct { + DB *sqlx.DB + mtx sync.RWMutex + GetDB func() *sqlx.DB + Name string +} + +func (s *StableSqlxDBWrapper) Query(query string, args ...any) (*sql.Rows, error) { + res, err := func() (*sql.Rows, error) { + s.mtx.RLock() + defer s.mtx.RUnlock() + res, err := s.DB.Query(query, args...) + return res, err + }() + if err != nil { + fmt.Println(err) + s.mtx.Lock() + defer s.mtx.Unlock() + s.DB.Close() + s.DB = s.GetDB() + } + return res, err +} + +func (s *StableSqlxDBWrapper) QueryCtx(ctx context.Context, query string, args ...any) (*sql.Rows, error) { + res, err := func() (*sql.Rows, error) { + s.mtx.RLock() + defer s.mtx.RUnlock() + res, err := s.DB.QueryContext(ctx, query, args...) + return res, err + }() + if err != nil { + fmt.Println(err) + s.mtx.Lock() + defer s.mtx.Unlock() + s.DB.Close() + s.DB = s.GetDB() + } + return res, err +} + +func (s *StableSqlxDBWrapper) ExecCtx(ctx context.Context, query string, args ...any) error { + err := func() error { + s.mtx.RLock() + defer s.mtx.RUnlock() + _, err := s.DB.ExecContext(ctx, query, args...) + return err + }() + if err != nil { + fmt.Println(err) + s.mtx.Lock() + defer s.mtx.Unlock() + s.DB.Close() + s.DB = s.GetDB() + } + return err +} + +func (s *StableSqlxDBWrapper) GetName() string { + return s.Name +} + +func (s *StableSqlxDBWrapper) Conn(ctx context.Context) (*sql.Conn, error) { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.DB.Conn(ctx) +} +func (s *StableSqlxDBWrapper) Begin() (*sql.Tx, error) { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.DB.Begin() +} + +func (s *StableSqlxDBWrapper) Close() { + s.mtx.RLock() + defer s.mtx.RUnlock() + s.DB.Close() +} diff --git a/reader/utils/logger/echologrus/echologrus.go b/reader/utils/logger/echologrus/echologrus.go new file mode 100644 index 00000000..74aa1de1 --- /dev/null +++ b/reader/utils/logger/echologrus/echologrus.go @@ -0,0 +1,147 @@ +package echologrus + +import ( + "io" + + "github.com/labstack/gommon/log" + "github.com/sirupsen/logrus" +) + +// Logrus : implement Logger +type Logrus struct { + *logrus.Logger +} + +// Logger ... +var Logger *logrus.Logger + +// GetEchoLogger for e.Logger +func GetEchoLogger() Logrus { + return Logrus{Logger} +} + +// Level returns logger level +func (l Logrus) Level() log.Lvl { + switch l.Logger.Level { + case logrus.DebugLevel: + return log.DEBUG + case logrus.WarnLevel: + return log.WARN + case logrus.ErrorLevel: + return log.ERROR + case logrus.InfoLevel: + return log.INFO + default: + l.Panic("Invalid level") + } + + return log.OFF +} + +// SetHeader is a stub to satisfy interface +// It's controlled by Logger +func (l Logrus) SetHeader(_ string) {} + +// SetPrefix It's controlled by Logger +func (l Logrus) SetPrefix(s string) {} + +// Prefix It's controlled by Logger +func (l Logrus) Prefix() string { + return "" +} + +// SetLevel set level to logger from given log.Lvl +func (l Logrus) SetLevel(lvl log.Lvl) { + switch lvl { + case log.DEBUG: + Logger.SetLevel(logrus.DebugLevel) + case log.WARN: + Logger.SetLevel(logrus.WarnLevel) + case log.ERROR: + Logger.SetLevel(logrus.ErrorLevel) + case log.INFO: + Logger.SetLevel(logrus.InfoLevel) + default: + l.Panic("Invalid level") + } +} + +// Output logger output func +func (l Logrus) Output() io.Writer { + return l.Out +} + +// SetOutput change output, default os.Stdout +func (l Logrus) SetOutput(w io.Writer) { + Logger.SetOutput(w) +} + +// Printj print json log +func (l Logrus) Printj(j log.JSON) { + Logger.WithFields(logrus.Fields(j)).Print() +} + +// Debugj debug json log +func (l Logrus) Debugj(j log.JSON) { + Logger.WithFields(logrus.Fields(j)).Debug() +} + +// Infoj info json log +func (l Logrus) Infoj(j log.JSON) { + Logger.WithFields(logrus.Fields(j)).Info() +} + +// Warnj warning json log +func (l Logrus) Warnj(j log.JSON) { + Logger.WithFields(logrus.Fields(j)).Warn() +} + +// Errorj error json log +func (l Logrus) Errorj(j log.JSON) { + Logger.WithFields(logrus.Fields(j)).Error() +} + +// Fatalj fatal json log +func (l Logrus) Fatalj(j log.JSON) { + Logger.WithFields(logrus.Fields(j)).Fatal() +} + +// Panicj panic json log +func (l Logrus) Panicj(j log.JSON) { + Logger.WithFields(logrus.Fields(j)).Panic() +} + +// Print string log +func (l Logrus) Print(i ...interface{}) { + Logger.Print(i[0].(string)) +} + +// Debug string log +func (l Logrus) Debug(i ...interface{}) { + Logger.Debug(i[0].(string)) +} + +// Info string log +func (l Logrus) Info(i ...interface{}) { + Logger.Info(i[0].(string)) +} + +// Warn string log +func (l Logrus) Warn(i ...interface{}) { + Logger.Warn(i[0].(string)) +} + +// Error string log +func (l Logrus) Error(i ...interface{}) { + Logger.Error(i[0].(string)) +} + +// Fatal string log +func (l Logrus) Fatal(i ...interface{}) { + Logger.Fatal(i[0].(string)) +} + +// Panic string log +func (l Logrus) Panic(i ...interface{}) { + Logger.Panic(i[0].(string)) +} diff --git a/reader/utils/logger/logger.go b/reader/utils/logger/logger.go new file mode 100644 index 00000000..e43cc330 --- /dev/null +++ b/reader/utils/logger/logger.go @@ -0,0 +1,300 @@ +package logger + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/metrico/qryn/reader/config" + "github.com/metrico/qryn/reader/system" + "log" + "log/syslog" + "net/http" + "os" + "path/filepath" + "strconv" + "strings" + "sync" + "time" + + rotatelogs "github.com/lestrrat-go/file-rotatelogs" + "github.com/sirupsen/logrus" +) + +type LogInfo logrus.Fields + +var RLogs *rotatelogs.RotateLogs +var Logger = logrus.New() + +type DbLogger struct{} + +/* db logger for logrus */ +func (*DbLogger) Print(v ...interface{}) { + if v[0] == "sql" { + Logger.WithFields(logrus.Fields{"module": "db", "type": "sql"}).Print(v[3]) + } + if v[0] == "log" { + Logger.WithFields(logrus.Fields{"module": "db", "type": "log"}).Print(v[2]) + } +} + +// initLogger function +func InitLogger() { + + //env := os.Getenv("environment") + //isLocalHost := env == "local" + if config.Cloki.Setting.LOG_SETTINGS.Json { + // Log as JSON instead of the default ASCII formatter. + Logger.SetFormatter(&logrus.JSONFormatter{}) + } else { + Logger.Formatter.(*logrus.TextFormatter).DisableTimestamp = false + Logger.Formatter.(*logrus.TextFormatter).DisableColors = true + } + + if config.Cloki.Setting.LOG_SETTINGS.Qryn.Url != "" { + hostname := "" + if config.Cloki.Setting.LOG_SETTINGS.Qryn.AddHostname { + hostname, _ = os.Hostname() + } + + headers := map[string]string{} + for _, h := range strings.Split(config.Cloki.Setting.LOG_SETTINGS.Qryn.Headers, ";;") { + pair := strings.Split(h, ":") + headers[pair[0]] = strings.Join(pair[1:], ":") + } + + qrynFmt := &qrynFormatter{ + formatter: Logger.Formatter, + url: config.Cloki.Setting.LOG_SETTINGS.Qryn.Url, + app: config.Cloki.Setting.LOG_SETTINGS.Qryn.App, + hostname: hostname, + headers: headers, + } + Logger.SetFormatter(qrynFmt) + qrynFmt.Run() + } + + // Output to stdout instead of the default stderr + // Can be any io.Writer, see below for File example + if config.Cloki.Setting.LOG_SETTINGS.Stdout { + Logger.SetOutput(os.Stdout) + log.SetOutput(os.Stdout) + } + + /* log level default */ + if config.Cloki.Setting.LOG_SETTINGS.Level == "" { + config.Cloki.Setting.LOG_SETTINGS.Level = "error" + } + + if logLevel, ok := logrus.ParseLevel(config.Cloki.Setting.LOG_SETTINGS.Level); ok == nil { + // Only log the warning severity or above. + Logger.SetLevel(logLevel) + } else { + Logger.Error("Couldn't parse loglevel", config.Cloki.Setting.LOG_SETTINGS.Level) + Logger.SetLevel(logrus.ErrorLevel) + } + + Logger.Info("init logging system") + + if !config.Cloki.Setting.LOG_SETTINGS.Stdout && !config.Cloki.Setting.LOG_SETTINGS.SysLog { + // configure file system hook + configureLocalFileSystemHook() + } else if !config.Cloki.Setting.LOG_SETTINGS.Stdout { + configureSyslogHook() + } +} + +// SetLoggerLevel function +func SetLoggerLevel(loglevelString string) { + + if logLevel, ok := logrus.ParseLevel(loglevelString); ok == nil { + // Only log the warning severity or above. + Logger.SetLevel(logLevel) + } else { + Logger.Error("Couldn't parse loglevel", loglevelString) + Logger.SetLevel(logrus.ErrorLevel) + } +} + +func configureLocalFileSystemHook() { + + logPath := config.Cloki.Setting.LOG_SETTINGS.Path + logName := config.Cloki.Setting.LOG_SETTINGS.Name + var err error + + if configPath := os.Getenv("WEBAPPLOGPATH"); configPath != "" { + logPath = configPath + } + + if configName := os.Getenv("WEBAPPLOGNAME"); configName != "" { + logName = configName + } + + fileLogExtension := filepath.Ext(logName) + fileLogBase := strings.TrimSuffix(logName, fileLogExtension) + + pathAllLog := logPath + "/" + fileLogBase + "_%Y%m%d%H%M" + fileLogExtension + pathLog := logPath + "/" + logName + + RLogs, err = rotatelogs.New( + pathAllLog, + rotatelogs.WithLinkName(pathLog), + rotatelogs.WithMaxAge(time.Duration(config.Cloki.Setting.LOG_SETTINGS.MaxAgeDays)*time.Hour), + rotatelogs.WithRotationTime(time.Duration(config.Cloki.Setting.LOG_SETTINGS.RotationHours)*time.Hour), + ) + + if err != nil { + Logger.Println("Local file system hook initialize fail") + return + } + + Logger.SetOutput(RLogs) + log.SetOutput(RLogs) + + /* + Logger.AddHook(lfshook.NewHook(lfshook.WriterMap{ + logrus.InfoLevel: rLogs, + logrus.DebugLevel: rLogs, + logrus.ErrorLevel: rLogs, + }, &logrus.JSONFormatter{})) + */ +} +func configureSyslogHook() { + + var err error + + Logger.Println("Init syslog...") + + sevceritySyslog := getSevirtyByName(config.Cloki.Setting.LOG_SETTINGS.SysLogLevel) + + syslogger, err := syslog.New(sevceritySyslog, "hepic-app-server") + + //hook, err := lSyslog.NewSyslogHook(proto, logSyslogUri, sevceritySyslog, "") + + if err != nil { + Logger.Println("Unable to connect to syslog:", err) + } + + Logger.SetOutput(syslogger) + log.SetOutput(syslogger) + + /* + Logger.AddHook(lfshook.NewHook(lfshook.WriterMap{ + logrus.InfoLevel: rLogs, + logrus.DebugLevel: rLogs, + logrus.ErrorLevel: rLogs, + }, &logrus.JSONFormatter{})) + */ +} + +func Info(args ...interface{}) { + Logger.Info(args...) +} + +func Error(args ...interface{}) { + Logger.Error(args...) +} + +func Debug(args ...interface{}) { + Logger.Debug(args...) +} + +func getSevirtyByName(sevirity string) syslog.Priority { + + switch sevirity { + case system.SYSLOG_LOG_EMERG: + return syslog.LOG_EMERG + case system.SYSLOG_LOG_ALERT: + return syslog.LOG_ALERT + case system.SYSLOG_LOG_CRIT: + return syslog.LOG_CRIT + case system.SYSLOG_LOG_ERR: + return syslog.LOG_ERR + case system.SYSLOG_LOG_WARNING: + return syslog.LOG_WARNING + case system.SYSLOG_LOG_NOTICE: + return syslog.LOG_NOTICE + case system.SYSLOG_LOG_INFO: + return syslog.LOG_INFO + case system.SYSLOG_LOG_DEBUG: + return syslog.LOG_DEBUG + default: + return syslog.LOG_INFO + + } +} + +type qrynFormatter struct { + mtx sync.Mutex + formatter logrus.Formatter + bufferToQryn []*logrus.Entry + timer *time.Ticker + url string + app string + hostname string + headers map[string]string +} + +type qrynLogs struct { + Stream map[string]string `json:"stream"` + Values [][]string `json:"values"` +} + +func (q *qrynFormatter) Format(e *logrus.Entry) ([]byte, error) { + q.mtx.Lock() + q.bufferToQryn = append(q.bufferToQryn, e) + q.mtx.Unlock() + return q.formatter.Format(e) +} + +func (q *qrynFormatter) Run() { + q.timer = time.NewTicker(time.Second) + go func() { + for range q.timer.C { + q.mtx.Lock() + bufferToQryn := q.bufferToQryn + q.bufferToQryn = nil + q.mtx.Unlock() + if len(bufferToQryn) < 1 { + continue + } + + streams := map[string]*qrynLogs{} + for _, e := range bufferToQryn { + stream := map[string]string{} + stream["app"] = q.app + if q.hostname != "" { + stream["hostname"] = q.hostname + } + stream["level"] = e.Level.String() + + strStream := fmt.Sprintf("%v", stream) + if _, ok := streams[strStream]; !ok { + streams[strStream] = &qrynLogs{Stream: stream} + } + + strValue, _ := q.formatter.Format(e) + streams[strStream].Values = append( + streams[strStream].Values, + []string{strconv.FormatInt(e.Time.UnixNano(), 10), string(strValue)}) + } + + var arrStreams []*qrynLogs + for _, s := range streams { + arrStreams = append(arrStreams, s) + } + + strStreams, _ := json.Marshal(map[string][]*qrynLogs{"streams": arrStreams}) + go func() { + req, _ := http.NewRequest("POST", q.url, bytes.NewReader(strStreams)) + if req == nil { + return + } + for k, v := range q.headers { + req.Header.Set(k, v) + } + req.Header.Set("Content-Type", "application/json") + http.DefaultClient.Do(req) + }() + } + }() +} diff --git a/reader/utils/middleware/accept_encoding.go b/reader/utils/middleware/accept_encoding.go new file mode 100644 index 00000000..aa2cb41e --- /dev/null +++ b/reader/utils/middleware/accept_encoding.go @@ -0,0 +1,88 @@ +package middleware + +import ( + "bufio" + "bytes" + "compress/gzip" + "errors" + "net" + "net/http" + "strconv" + "strings" +) + +func AcceptEncodingMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { + gzw := newGzipResponseWriter(w) + defer gzw.Close() + next.ServeHTTP(gzw, r) + return + } + next.ServeHTTP(w, r) + }) +} + +// gzipResponseWriter wraps the http.ResponseWriter to provide gzip functionality +type gzipResponseWriter struct { + http.ResponseWriter + Writer *gzip.Writer + code int + codeSet bool + written int + preBuffer bytes.Buffer +} + +func newGzipResponseWriter(w http.ResponseWriter) *gzipResponseWriter { + res := &gzipResponseWriter{ + ResponseWriter: w, + code: 200, + } + gz := gzip.NewWriter(&res.preBuffer) + res.Writer = gz + return res +} + +func (gzw *gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + h, ok := gzw.ResponseWriter.(http.Hijacker) + if !ok { + return nil, nil, errors.New("ResponseWriter does not support Hijack") + } + return h.Hijack() +} + +func (gzw *gzipResponseWriter) WriteHeader(code int) { + if gzw.codeSet { + return + } + gzw.codeSet = true + gzw.code = code + if gzw.code/100 == 2 { + gzw.Header().Set("Content-Encoding", "gzip") + } else { + gzw.ResponseWriter.WriteHeader(code) + } + +} + +func (gzw *gzipResponseWriter) Write(b []byte) (int, error) { + gzw.codeSet = true + if gzw.code/100 == 2 { + gzw.Header().Set("Content-Encoding", "gzip") + gzw.written += len(b) + return gzw.Writer.Write(b) + } + return gzw.ResponseWriter.Write(b) +} + +func (gzw *gzipResponseWriter) Close() { + if gzw.written > 0 { + gzw.Writer.Close() + } + if gzw.code/100 != 2 { + return + } + gzw.Header().Set("Content-Length", strconv.Itoa(gzw.preBuffer.Len())) + gzw.ResponseWriter.WriteHeader(gzw.code) + gzw.ResponseWriter.Write(gzw.preBuffer.Bytes()) +} diff --git a/reader/utils/middleware/basic_auth.go b/reader/utils/middleware/basic_auth.go new file mode 100644 index 00000000..90f9e500 --- /dev/null +++ b/reader/utils/middleware/basic_auth.go @@ -0,0 +1,36 @@ +package middleware + +import ( + "encoding/base64" + "net/http" + "strings" +) + +func BasicAuthMiddleware(login, pass string) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + auth := r.Header.Get("Authorization") + if auth == "" { + w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + authParts := strings.SplitN(auth, " ", 2) + if len(authParts) != 2 || authParts[0] != "Basic" { + http.Error(w, "Invalid authorization header", http.StatusBadRequest) + return + } + + payload, _ := base64.StdEncoding.DecodeString(authParts[1]) + pair := strings.SplitN(string(payload), ":", 2) + + if len(pair) != 2 || pair[0] != login || + pair[1] != pass { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + next.ServeHTTP(w, r) + }) + } +} diff --git a/reader/utils/middleware/cors_middleware.go b/reader/utils/middleware/cors_middleware.go new file mode 100644 index 00000000..37ec64ca --- /dev/null +++ b/reader/utils/middleware/cors_middleware.go @@ -0,0 +1,19 @@ +package middleware + +import "net/http" + +func CorsMiddleware(allowOrigin string) func(handler http.Handler) http.Handler { + if allowOrigin == "" { + allowOrigin = "*" + } + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) { + w.Header().Set("Access-Control-Allow-Headers", + "Origin,Content-Type,Accept,Content-Length,Accept-Language,Accept-Encoding,Connection,Access-Control-Allow-Origin") + w.Header().Set("Access-Control-Allow-Origin", allowOrigin) + w.Header().Set("Access-Control-Allow-Methods", "GET,POST,HEAD,PUT,DELETE,PATCH,OPTIONS") + w.Header().Set("Access-Control-Allow-Credentials", "true") + next.ServeHTTP(w, request) + }) + } +} diff --git a/reader/utils/middleware/logging.go b/reader/utils/middleware/logging.go new file mode 100644 index 00000000..b31229d7 --- /dev/null +++ b/reader/utils/middleware/logging.go @@ -0,0 +1,66 @@ +package middleware +import "html" + +import ( + "bufio" + "bytes" + "errors" + "github.com/metrico/qryn/reader/utils/logger" + "net" + "net/http" + "text/template" + "time" +) + +func LoggingMiddleware(tpl string) func(next http.Handler) http.Handler { + t := template.New("http-logging") + t.Parse(tpl) + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // TODO: Log the request details using the template + _w := &responseWriterWithCode{ResponseWriter: w, statusCode: http.StatusOK} + start := time.Now() + //t.Execute(w, r) + next.ServeHTTP(_w, r) + duration := time.Since(start) + b := bytes.NewBuffer(nil) + t.Execute(b, map[string]any{ + "method": html.EscapeString(r.Method), + "url": html.EscapeString(r.URL.String()), + "proto": html.EscapeString(r.Proto), + "status": _w.statusCode, + "length": _w.length, + "referer": html.EscapeString(r.Referer()), + "user_agent": html.EscapeString(r.UserAgent()), + "host": html.EscapeString(r.Host), + "path": html.EscapeString(r.URL.Path), + "latency": duration.String(), + }) + logger.Info(string(b.Bytes())) + }) + } +} + +type responseWriterWithCode struct { + http.ResponseWriter + statusCode int + length int +} + +func (w *responseWriterWithCode) Hijack() (net.Conn, *bufio.ReadWriter, error) { + h, ok := w.ResponseWriter.(http.Hijacker) + if !ok { + return nil, nil, errors.New("ResponseWriter does not support Hijack") + } + return h.Hijack() +} + +func (w *responseWriterWithCode) WriteHeader(code int) { + w.statusCode = code + w.ResponseWriter.WriteHeader(code) +} + +func (w *responseWriterWithCode) Write(b []byte) (int, error) { + w.length += len(b) + return w.ResponseWriter.Write(b) +} diff --git a/reader/utils/shutdown/shutdown.go b/reader/utils/shutdown/shutdown.go new file mode 100644 index 00000000..fce19f7b --- /dev/null +++ b/reader/utils/shutdown/shutdown.go @@ -0,0 +1,7 @@ +package shutdown + +func Shutdown(code int) { + Chan <- code +} + +var Chan = make(chan int) diff --git a/wasm_parts/sql_select/condition.go b/reader/utils/sql_select/condition.go similarity index 100% rename from wasm_parts/sql_select/condition.go rename to reader/utils/sql_select/condition.go diff --git a/wasm_parts/sql_select/iface.go b/reader/utils/sql_select/iface.go similarity index 91% rename from wasm_parts/sql_select/iface.go rename to reader/utils/sql_select/iface.go index a09a0a27..65339ae7 100644 --- a/wasm_parts/sql_select/iface.go +++ b/reader/utils/sql_select/iface.go @@ -29,6 +29,13 @@ func (c *Ctx) Id() int { return c.id } +func DefaultCtx() *Ctx { + return &Ctx{ + Params: make(map[string]SQLObject), + Result: make(map[string]SQLObject), + } +} + type ISelect interface { Distinct(distinct bool) ISelect GetDistinct() bool @@ -45,6 +52,7 @@ type ISelect interface { AndHaving(clauses ...SQLCondition) ISelect OrHaving(clauses ...SQLCondition) ISelect GetHaving() SQLCondition + SetHaving(having SQLCondition) ISelect GroupBy(fields ...SQLObject) ISelect GetGroupBy() []SQLObject OrderBy(fields ...SQLObject) ISelect diff --git a/wasm_parts/sql_select/objects.go b/reader/utils/sql_select/objects.go similarity index 100% rename from wasm_parts/sql_select/objects.go rename to reader/utils/sql_select/objects.go diff --git a/wasm_parts/sql_select/select.go b/reader/utils/sql_select/select.go similarity index 94% rename from wasm_parts/sql_select/select.go rename to reader/utils/sql_select/select.go index c1669a70..0593c104 100644 --- a/wasm_parts/sql_select/select.go +++ b/reader/utils/sql_select/select.go @@ -178,6 +178,11 @@ func (s *Select) GetHaving() SQLCondition { return s.having } +func (s *Select) SetHaving(having SQLCondition) ISelect { + s.having = having + return s +} + func (s *Select) GroupBy(fields ...SQLObject) ISelect { s.groupBy = fields return s @@ -235,8 +240,8 @@ func (s *Select) AddWith(withs ...*With) ISelect { continue } - if _, ok := w.GetQuery().(*Select); ok { - s.AddWith(w.GetQuery().(*Select).GetWith()...) + if _, ok := w.GetQuery().(ISelect); ok { + s.AddWith(w.GetQuery().(ISelect).GetWith()...) } s.withs = append(s.withs, w) } @@ -322,22 +327,25 @@ func (s *Select) String(ctx *Ctx, options ...int) (string, error) { } res.WriteString(str) } - if s.from == nil { - return "", fmt.Errorf("no 'FROM' part") - } - res.WriteString(" FROM ") - str, err := s.from.String(ctx, options...) - if err != nil { - return "", err - } - res.WriteString(str) - for _, lj := range s.joins { - res.WriteString(fmt.Sprintf(" %s JOIN ", lj.tp)) - str, err = lj.String(ctx, options...) + var ( + str string + err error + ) + if s.from != nil { + res.WriteString(" FROM ") + str, err = s.from.String(ctx, options...) if err != nil { return "", err } res.WriteString(str) + for _, lj := range s.joins { + res.WriteString(fmt.Sprintf(" %s JOIN ", lj.tp)) + str, err = lj.String(ctx, options...) + if err != nil { + return "", err + } + res.WriteString(str) + } } if s.preWhere != nil { res.WriteString(" PREWHERE ") diff --git a/reader/utils/tables/tables.go b/reader/utils/tables/tables.go new file mode 100644 index 00000000..c00c0928 --- /dev/null +++ b/reader/utils/tables/tables.go @@ -0,0 +1,92 @@ +package tables + +import ( + "fmt" + "github.com/metrico/qryn/reader/logql/logql_transpiler_v2/shared" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/plugins" + "sync" +) + +var tableNames = func() map[string]string { + return map[string]string{} +}() +var lock sync.RWMutex + +func init() { + lock.Lock() + defer lock.Unlock() + + tableNames["tempo_traces"] = "tempo_traces" + tableNames["tempo_traces_dist"] = "tempo_traces_dist" + tableNames["tempo_traces_kv"] = "tempo_traces_kv" + tableNames["tempo_traces_kv_dist"] = "tempo_traces_kv_dist" + tableNames["time_series"] = "time_series" + tableNames["time_series_dist"] = "time_series_dist" + tableNames["samples_kv"] = "samples_kv" + tableNames["samples_kv_dist"] = "samples_kv_dist" + tableNames["time_series_gin"] = "time_series_gin" + tableNames["time_series_gin_dist"] = "time_series_gin_dist" + tableNames["samples_v3"] = "samples_v3" + tableNames["samples_v3_dist"] = "samples_v3_dist" + tableNames["metrics_15s"] = "metrics_15s" + tableNames["profiles_series"] = "profiles_series" + tableNames["profiles_series_gin"] = "profiles_series_gin" + tableNames["profiles"] = "profiles" +} + +func GetTableName(name string) string { + lock.RLock() + defer lock.RUnlock() + p := plugins.GetTableNamesPlugin() + if p == nil { + return tableNames[name] + } + n := (*p)()[name] + if n == "" { + return tableNames[name] + } + return n +} + +func PopulateTableNames(ctx *shared.PlannerContext, db *model.DataDatabasesMap) *shared.PlannerContext { + tsGinTable := GetTableName("time_series_gin") + samplesTableName := GetTableName("samples_v3") + timeSeriesTableName := GetTableName("time_series") + timeSeriesDistTableName := GetTableName("time_series") + metrics15sTableName := GetTableName("metrics_15s") + + ctx.ProfilesSeriesGinTable = GetTableName("profiles_series_gin") + ctx.ProfilesSeriesGinDistTable = GetTableName("profiles_series_gin") + ctx.ProfilesTable = GetTableName("profiles") + ctx.ProfilesDistTable = GetTableName("profiles") + ctx.ProfilesSeriesTable = GetTableName("profiles_series") + ctx.ProfilesSeriesDistTable = GetTableName("profiles_series") + + ctx.TracesAttrsTable = GetTableName("tempo_traces_attrs_gin") + ctx.TracesAttrsDistTable = GetTableName("tempo_traces_attrs_gin") + ctx.TracesTable = GetTableName("tempo_traces") + ctx.TracesDistTable = GetTableName("tempo_traces") + ctx.TracesKVTable = GetTableName("tempo_traces_kv") + ctx.TracesKVDistTable = GetTableName("tempo_traces_kv") + + if db.Config.ClusterName != "" { + tsGinTable = fmt.Sprintf("`%s`.%s", db.Config.Name, tsGinTable) + samplesTableName = fmt.Sprintf("`%s`.%s_dist", db.Config.Name, samplesTableName) + timeSeriesTableName = fmt.Sprintf("`%s`.%s", db.Config.Name, timeSeriesTableName) + timeSeriesDistTableName = fmt.Sprintf("`%s`.%s_dist", db.Config.Name, timeSeriesDistTableName) + metrics15sTableName = fmt.Sprintf("`%s`.%s_dist", db.Config.Name, metrics15sTableName) + ctx.ProfilesSeriesGinDistTable = fmt.Sprintf("`%s`.%s_dist", db.Config.Name, ctx.ProfilesSeriesGinTable) + ctx.ProfilesDistTable = fmt.Sprintf("`%s`.%s_dist", db.Config.Name, ctx.ProfilesTable) + ctx.ProfilesSeriesDistTable = fmt.Sprintf("`%s`.%s_dist", db.Config.Name, ctx.ProfilesSeriesTable) + ctx.TracesAttrsDistTable = fmt.Sprintf("`%s`.%s_dist", db.Config.Name, ctx.TracesAttrsTable) + ctx.TracesDistTable = fmt.Sprintf("`%s`.%s_dist", db.Config.Name, ctx.TracesTable) + ctx.TracesKVDistTable = fmt.Sprintf("`%s`.%s_dist", db.Config.Name, ctx.TracesKVTable) + } + ctx.TimeSeriesGinTableName = tsGinTable + ctx.SamplesTableName = samplesTableName + ctx.TimeSeriesTableName = timeSeriesTableName + ctx.TimeSeriesDistTableName = timeSeriesDistTableName + ctx.Metrics15sTableName = metrics15sTableName + return ctx +} diff --git a/reader/utils/unmarshal/convert.go b/reader/utils/unmarshal/convert.go new file mode 100644 index 00000000..81711135 --- /dev/null +++ b/reader/utils/unmarshal/convert.go @@ -0,0 +1,73 @@ +package unmarshal + +import ( + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "github.com/metrico/qryn/reader/model" + v12 "go.opentelemetry.io/proto/otlp/common/v1" + v1 "go.opentelemetry.io/proto/otlp/trace/v1" +) + +func SpanToJSONSpan(span *v1.Span) *model.JSONSpan { + res := &model.JSONSpan{ + TraceID: hex.EncodeToString(span.TraceId), + TraceId: hex.EncodeToString(span.TraceId), + SpanID: hex.EncodeToString(span.SpanId), + SpanId: hex.EncodeToString(span.SpanId), + Name: span.Name, + StartTimeUnixNano: span.StartTimeUnixNano, + EndTimeUnixNano: span.EndTimeUnixNano, + ServiceName: "", + Attributes: make([]model.JSONSpanAttribute, len(span.Attributes)), + Events: make([]model.JSONSpanEvent, len(span.Events)), + Status: span.Status, + } + for i, attr := range span.Attributes { + _attr := model.JSONSpanAttribute{ + Key: attr.Key, + Value: struct { + StringValue string `json:"stringValue"` + }{}, + } + switch attr.Value.Value.(type) { + case *v12.AnyValue_StringValue: + _attr.Value.StringValue = attr.Value.GetStringValue() + break + case *v12.AnyValue_BoolValue: + _attr.Value.StringValue = fmt.Sprintf("%v", attr.Value.GetBoolValue()) + break + case *v12.AnyValue_IntValue: + _attr.Value.StringValue = fmt.Sprintf("%v", attr.Value.GetIntValue()) + break + case *v12.AnyValue_DoubleValue: + _attr.Value.StringValue = fmt.Sprintf("%v", attr.Value.GetDoubleValue()) + break + case *v12.AnyValue_BytesValue: + _attr.Value.StringValue = base64.StdEncoding.EncodeToString(attr.Value.GetBytesValue()) + break + default: + bVal, _ := json.Marshal(attr.Value.Value) + _attr.Value.StringValue = string(bVal) + break + } + res.Attributes[i] = _attr + } + for _, attr := range span.Attributes { + if attr.Key == "service.name" && attr.Value.GetStringValue() != "" { + res.ServiceName = attr.Value.GetStringValue() + } + } + if len(span.ParentSpanId) > 0 && hex.EncodeToString(span.ParentSpanId) != "0000000000000000" { + res.ParentSpanId = hex.EncodeToString(span.ParentSpanId) + } + for i, evt := range span.Events { + res.Events[i] = model.JSONSpanEvent{ + TimeUnixNano: evt.TimeUnixNano, + Name: evt.Name, + } + + } + return res +} diff --git a/reader/utils/unmarshal/legacy/unmarshal.go b/reader/utils/unmarshal/legacy/unmarshal.go new file mode 100644 index 00000000..95cf0bfb --- /dev/null +++ b/reader/utils/unmarshal/legacy/unmarshal.go @@ -0,0 +1,29 @@ +package unmarshal + +import ( + "io" + + json "github.com/json-iterator/go" +) + +// DecodePushRequest directly decodes json to a logproto.PushRequest +func DecodePushRequest(b io.Reader, r *model.PushRequest) error { + return json.NewDecoder(b).Decode(r) +} + +// DecodePushRequest directly decodes json to a logproto.PushRequest +func DecodePushRequestString(body []byte) (model.PushRequest, error) { + + request := model.PushRequest{} + + if err := json.Unmarshal(body, &request); err != nil { + return request, err + } + + /*if err := json.Unmarshal(body, r); err != nil { + return err + } + */ + + return request, nil +} diff --git a/reader/utils/unmarshal/unmarshal.go b/reader/utils/unmarshal/unmarshal.go new file mode 100644 index 00000000..5325dfc4 --- /dev/null +++ b/reader/utils/unmarshal/unmarshal.go @@ -0,0 +1,114 @@ +package unmarshal + +import ( + "github.com/metrico/qryn/reader/model" + "io" + "strings" + + jsoniter "github.com/json-iterator/go" +) + +var jsonApi = jsoniter.ConfigCompatibleWithStandardLibrary + +// DecodePushRequest directly decodes json to a logproto.PushRequest +func DecodePushRequest(b io.Reader) (model.PushRequest, error) { + + request := model.PushRequest{} + + if err := jsoniter.NewDecoder(b).Decode(&request); err != nil { + return request, err + } + + return request, nil +} + +// DecodePushRequest directly decodes json to a logproto.PushRequest +func DecodePushRequestString(body []byte) (model.PushRequest, error) { + + request := model.PushRequest{} + + if err := jsonApi.Unmarshal(body, &request); err != nil { + return request, err + } + + return request, nil +} + +// DecodePushRequest directly decodes json to a logproto.PushRequest +func MarshalLabelsPushRequestString(labels []model.Label) ([]byte, error) { + + strArr := []string{} + + for _, s := range labels { + strArr = append(strArr, s.Key+"=\""+s.Value+"\"") + } + + return []byte(strings.Join(strArr, ",")), nil +} + +// DecodePushRequest directly decodes json to a logproto.PushRequest +func MarshalArrayLabelsPushRequestString(labels []string) ([]byte, error) { + + data, err := jsonApi.Marshal(labels) + if err != nil { + return nil, err + } + return data, err +} + +/* +// NewPushRequest constructs a logproto.PushRequest from a PushRequest +func NewPushRequest(r loghttp.PushRequest) logproto.PushRequest { + ret := logproto.PushRequest{ + Streams: make([]logproto.Stream, len(r.Streams)), + } + + for i, s := range r.Streams { + ret.Streams[i] = NewStream(s) + } + + return ret +} + +// NewPushRequest constructs a logproto.PushRequest from a PushRequest +func NewPushRequestLog(r model.PushRequest) logproto.PushRequest { + ret := logproto.PushRequest{ + Streams: make([]logproto.Stream, len(r.Streams)), + } + for i, s := range r.Streams { + ret.Streams[i] = NewStreamLog(&s) + } + + return ret +} + +// NewStream constructs a logproto.Stream from a Stream +func NewStream(s *loghttp.Stream) logproto.Stream { + return logproto.Stream{ + Entries: *(*[]logproto.Entry)(unsafe.Pointer(&s.Entries)), + Labels: s.Labels.String(), + } +} + +// NewStream constructs a logproto.Stream from a Stream +func NewStreamLog(s *model.Stream) logproto.Stream { + return logproto.Stream{ + Entries: *(*[]logproto.Entry)(unsafe.Pointer(&s.Entries)), + Labels: s.Labels, + } +} + +// WebsocketReader knows how to read message to a websocket connection. +type WebsocketReader interface { + ReadMessage() (int, []byte, error) +} + +// ReadTailResponseJSON unmarshals the loghttp.TailResponse from a websocket reader. +func ReadTailResponseJSON(r *loghttp.TailResponse, reader WebsocketReader) error { + _, data, err := reader.ReadMessage() + if err != nil { + return err + } + return jsoniter.Unmarshal(data, r) +} +*/ diff --git a/reader/watchdog/watchdog.go b/reader/watchdog/watchdog.go new file mode 100644 index 00000000..512c9e45 --- /dev/null +++ b/reader/watchdog/watchdog.go @@ -0,0 +1,41 @@ +package watchdog + +import ( + "fmt" + "github.com/metrico/qryn/reader/model" + "github.com/metrico/qryn/reader/utils/logger" + "time" +) + +var svc *model.ServiceData +var retries = 0 +var lastSuccessfulCheck = time.Now() + +func Init(_svc *model.ServiceData) { + svc = _svc + ticker := time.NewTicker(time.Second * 5) + go func() { + for _ = range ticker.C { + err := svc.Ping() + if err == nil { + retries = 0 + lastSuccessfulCheck = time.Now() + logger.Info("---- WATCHDOG CHECK OK ----") + continue + } + retries++ + logger.Info("---- WATCHDOG REPORT ----") + logger.Error("database not responding ", retries*5, " seconds") + if retries > 5 { + panic("WATCHDOG PANIC: database not responding") + } + } + }() +} + +func Check() error { + if lastSuccessfulCheck.Add(time.Second * 30).After(time.Now()) { + return nil + } + return fmt.Errorf("database not responding since %v", lastSuccessfulCheck) +} diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 00000000..88aaa89f --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,21 @@ +# folders description + +## ./deploy + +script files to build deployment (production and development version) + +### ./deploy/docker + +to build docker for production (with licensing on) + +### ./deploy/production/package + +to build deb & rpm for production (with licensing on) + +## ./test + +scripts to run tests (based on the dev versions of docker images) + +## ./test/e2e + +scripts to run end-to-end tests. Mandatory before every merge to the main branch. diff --git a/scripts/deploy/docker/Dockerfile b/scripts/deploy/docker/Dockerfile new file mode 100644 index 00000000..0b918296 --- /dev/null +++ b/scripts/deploy/docker/Dockerfile @@ -0,0 +1,19 @@ +FROM golang:1.23.4-alpine as builder + +COPY . /src + +WORKDIR /src + +ARG VIEW + +RUN if [ "$VIEW" = "1" ]; then \ + go build -tags view -o gigapipe . ; \ + else \ + go build -o gigapipe . ; \ + fi + +FROM alpine:3.21 + +COPY --from=builder /src/gigapipe /gigapipe + +CMD /gigapipe \ No newline at end of file diff --git a/docker/e2e/config.xml b/scripts/test/e2e/clickhouse/config.xml similarity index 91% rename from docker/e2e/config.xml rename to scripts/test/e2e/clickhouse/config.xml index 3020ed6d..2a99d191 100644 --- a/docker/e2e/config.xml +++ b/scripts/test/e2e/clickhouse/config.xml @@ -22,7 +22,7 @@ [1]: https://github.com/pocoproject/poco/blob/poco-1.9.4-release/Foundation/include/Poco/Logger.h#L105-L114 --> - <level>error</level> + <level>trace</level> <console>1</console> <!-- Rotation policy See https://github.com/pocoproject/poco/blob/poco-1.9.4-release/Foundation/include/Poco/FileChannel.h#L54-L85 @@ -621,20 +621,143 @@ https://clickhouse.com/docs/en/operations/table_engines/distributed/ --> <remote_servers> + <!-- Test only shard config for testing distributed storage --> + <test_shard_localhost> + <!-- Inter-server per-cluster secret for Distributed queries + default: no secret (no authentication will be performed) + + If set, then Distributed queries will be validated on shards, so at least: + - such cluster should exist on the shard, + - such cluster should have the same secret. + + And also (and which is more important), the initial_user will + be used as current user for the query. + + Right now the protocol is pretty simple and it only takes into account: + - cluster name + - query + + Also it will be nice if the following will be implemented: + - source hostname (see interserver_http_host), but then it will depends from DNS, + it can use IP address instead, but then the you need to get correct on the initiator node. + - target hostname / ip address (same notes as for source hostname) + - time-based security tokens + --> + <!-- <secret></secret> --> + + <shard> + <!-- Optional. Whether to write data to just one of the replicas. Default: false (write data to all replicas). --> + <!-- <internal_replication>false</internal_replication> --> + <!-- Optional. Shard weight when writing data. Default: 1. --> + <!-- <weight>1</weight> --> + <replica> + <host>localhost</host> + <port>9000</port> + <!-- Optional. Priority of the replica for load_balancing. Default: 1 (less value has more priority). --> + <!-- <priority>1</priority> --> + </replica> + </shard> + </test_shard_localhost> + <test_cluster_one_shard_three_replicas_localhost> + <shard> + <internal_replication>false</internal_replication> + <replica> + <host>127.0.0.1</host> + <port>9000</port> + </replica> + <replica> + <host>127.0.0.2</host> + <port>9000</port> + </replica> + <replica> + <host>127.0.0.3</host> + <port>9000</port> + </replica> + </shard> + <!--shard> + <internal_replication>false</internal_replication> + <replica> + <host>127.0.0.1</host> + <port>9000</port> + </replica> + <replica> + <host>127.0.0.2</host> + <port>9000</port> + </replica> + <replica> + <host>127.0.0.3</host> + <port>9000</port> + </replica> + </shard--> + </test_cluster_one_shard_three_replicas_localhost> + <test_cluster_two_shards_localhost> + <shard> + <replica> + <host>localhost</host> + <port>9000</port> + </replica> + </shard> + <shard> + <replica> + <host>localhost</host> + <port>9000</port> + </replica> + </shard> + </test_cluster_two_shards_localhost> <test_cluster_two_shards> <shard> <replica> - <host>clickhouse.cloki.deploy</host> + <host>e2e.clickhouse</host> <port>9000</port> </replica> </shard> <shard> <replica> - <host>clickhouse2.cloki.deploy</host> + <host>e2e.clickhouse2</host> <port>9000</port> </replica> </shard> </test_cluster_two_shards> + <test_cluster_two_shards_internal_replication> + <shard> + <internal_replication>true</internal_replication> + <replica> + <host>clickhouse_1</host> + <port>9000</port> + </replica> + </shard> + <shard> + <internal_replication>true</internal_replication> + <replica> + <host>clickhouse_2</host> + <port>9000</port> + </replica> + </shard> + </test_cluster_two_shards_internal_replication> + <test_shard_localhost_secure> + <shard> + <replica> + <host>localhost</host> + <port>9440</port> + <secure>1</secure> + </replica> + </shard> + </test_shard_localhost_secure> + <test_unavailable_shard> + <shard> + <replica> + <host>localhost</host> + <port>9000</port> + </replica> + </shard> + <shard> + <replica> + <host>localhost</host> + <port>1</port> + </replica> + </shard> + </test_unavailable_shard> + </remote_servers> <!-- The list of hosts allowed to use in URL-related storage engines and table functions. @@ -1004,12 +1127,12 @@ <raft_configuration> <server> <id>1</id> - <hostname>clickhouse.cloki.deploy</hostname> + <hostname>e2e.clickhouse</hostname> <port>9444</port> </server> <server> <id>2</id> - <hostname>clickhouse2.cloki.deploy</hostname> + <hostname>e2e.clickhouse2</hostname> <port>9444</port> </server> </raft_configuration> @@ -1017,11 +1140,11 @@ <zookeeper> <node> - <host>clickhouse.cloki.deploy</host> + <host>e2e.clickhouse</host> <port>2181</port> </node> <node> - <host>clickhouse2.cloki.deploy</host> + <host>e2e.clickhouse2</host> <port>2181</port> </node> </zookeeper> @@ -1070,8 +1193,8 @@ The same for max_partition_size_to_drop. Uncomment to disable protection. --> - <max_table_size_to_drop>0</max_table_size_to_drop> - <max_partition_size_to_drop>0</max_partition_size_to_drop> + <!-- <max_table_size_to_drop>0</max_table_size_to_drop> --> + <!-- <max_partition_size_to_drop>0</max_partition_size_to_drop> --> <!-- Example of parameters for GraphiteMergeTree table engine --> <graphite_rollup_example> diff --git a/docker/e2e/config2.xml b/scripts/test/e2e/clickhouse/config2.xml similarity index 91% rename from docker/e2e/config2.xml rename to scripts/test/e2e/clickhouse/config2.xml index 9d047207..b6aed314 100644 --- a/docker/e2e/config2.xml +++ b/scripts/test/e2e/clickhouse/config2.xml @@ -22,7 +22,7 @@ [1]: https://github.com/pocoproject/poco/blob/poco-1.9.4-release/Foundation/include/Poco/Logger.h#L105-L114 --> - <level>error</level> + <level>trace</level> <console>1</console> <!-- Rotation policy See https://github.com/pocoproject/poco/blob/poco-1.9.4-release/Foundation/include/Poco/FileChannel.h#L54-L85 @@ -621,20 +621,143 @@ https://clickhouse.com/docs/en/operations/table_engines/distributed/ --> <remote_servers> + <!-- Test only shard config for testing distributed storage --> + <test_shard_localhost> + <!-- Inter-server per-cluster secret for Distributed queries + default: no secret (no authentication will be performed) + + If set, then Distributed queries will be validated on shards, so at least: + - such cluster should exist on the shard, + - such cluster should have the same secret. + + And also (and which is more important), the initial_user will + be used as current user for the query. + + Right now the protocol is pretty simple and it only takes into account: + - cluster name + - query + + Also it will be nice if the following will be implemented: + - source hostname (see interserver_http_host), but then it will depends from DNS, + it can use IP address instead, but then the you need to get correct on the initiator node. + - target hostname / ip address (same notes as for source hostname) + - time-based security tokens + --> + <!-- <secret></secret> --> + + <shard> + <!-- Optional. Whether to write data to just one of the replicas. Default: false (write data to all replicas). --> + <!-- <internal_replication>false</internal_replication> --> + <!-- Optional. Shard weight when writing data. Default: 1. --> + <!-- <weight>1</weight> --> + <replica> + <host>localhost</host> + <port>9000</port> + <!-- Optional. Priority of the replica for load_balancing. Default: 1 (less value has more priority). --> + <!-- <priority>1</priority> --> + </replica> + </shard> + </test_shard_localhost> + <test_cluster_one_shard_three_replicas_localhost> + <shard> + <internal_replication>false</internal_replication> + <replica> + <host>127.0.0.1</host> + <port>9000</port> + </replica> + <replica> + <host>127.0.0.2</host> + <port>9000</port> + </replica> + <replica> + <host>127.0.0.3</host> + <port>9000</port> + </replica> + </shard> + <!--shard> + <internal_replication>false</internal_replication> + <replica> + <host>127.0.0.1</host> + <port>9000</port> + </replica> + <replica> + <host>127.0.0.2</host> + <port>9000</port> + </replica> + <replica> + <host>127.0.0.3</host> + <port>9000</port> + </replica> + </shard--> + </test_cluster_one_shard_three_replicas_localhost> + <test_cluster_two_shards_localhost> + <shard> + <replica> + <host>localhost</host> + <port>9000</port> + </replica> + </shard> + <shard> + <replica> + <host>localhost</host> + <port>9000</port> + </replica> + </shard> + </test_cluster_two_shards_localhost> <test_cluster_two_shards> <shard> <replica> - <host>clickhouse.cloki.deploy</host> + <host>e2e.clickhouse</host> <port>9000</port> </replica> </shard> <shard> <replica> - <host>clickhouse2.cloki.deploy</host> + <host>e2e.clickhouse2</host> <port>9000</port> </replica> </shard> </test_cluster_two_shards> + <test_cluster_two_shards_internal_replication> + <shard> + <internal_replication>true</internal_replication> + <replica> + <host>clickhouse_1</host> + <port>9000</port> + </replica> + </shard> + <shard> + <internal_replication>true</internal_replication> + <replica> + <host>clickhouse_2</host> + <port>9000</port> + </replica> + </shard> + </test_cluster_two_shards_internal_replication> + <test_shard_localhost_secure> + <shard> + <replica> + <host>localhost</host> + <port>9440</port> + <secure>1</secure> + </replica> + </shard> + </test_shard_localhost_secure> + <test_unavailable_shard> + <shard> + <replica> + <host>localhost</host> + <port>9000</port> + </replica> + </shard> + <shard> + <replica> + <host>localhost</host> + <port>1</port> + </replica> + </shard> + </test_unavailable_shard> + </remote_servers> <!-- The list of hosts allowed to use in URL-related storage engines and table functions. @@ -1004,12 +1127,12 @@ <raft_configuration> <server> <id>1</id> - <hostname>clickhouse.cloki.deploy</hostname> + <hostname>e2e.clickhouse</hostname> <port>9444</port> </server> <server> <id>2</id> - <hostname>clickhouse2.cloki.deploy</hostname> + <hostname>e2e.clickhouse2</hostname> <port>9444</port> </server> </raft_configuration> @@ -1017,11 +1140,11 @@ <zookeeper> <node> - <host>clickhouse.cloki.deploy</host> + <host>e2e.clickhouse</host> <port>2181</port> </node> <node> - <host>clickhouse2.cloki.deploy</host> + <host>e2e.clickhouse2</host> <port>2181</port> </node> </zookeeper> @@ -1070,8 +1193,8 @@ The same for max_partition_size_to_drop. Uncomment to disable protection. --> - <max_table_size_to_drop>0</max_table_size_to_drop> - <max_partition_size_to_drop>0</max_partition_size_to_drop> + <!-- <max_table_size_to_drop>0</max_table_size_to_drop> --> + <!-- <max_partition_size_to_drop>0</max_partition_size_to_drop> --> <!-- Example of parameters for GraphiteMergeTree table engine --> <graphite_rollup_example> diff --git a/scripts/test/e2e/docker-compose.yml b/scripts/test/e2e/docker-compose.yml new file mode 100644 index 00000000..f0110ba9 --- /dev/null +++ b/scripts/test/e2e/docker-compose.yml @@ -0,0 +1,58 @@ +# small setup for e2e tests +version: '2.1' +networks: + common: + driver: bridge +services: + e2e-aio: + image: gigapipe:latest + container_name: e2e.aio + hostname: e2e.aio + expose: + - 9080 + networks: + - common + ports: + - '3102:9080' + environment: + CLUSTER_NAME: "test_cluster_two_shards" + CLICKHOUSE_SERVER: "e2e.clickhouse" + QRYN_LOGIN: a + QRYN_PASSWORD: b + BULK_MAX_AGE_MS: "100" + PORT: "9080" + command: + - sh + - -c + - sleep 15 && /gigapipe + e2e-clickhouse: + image: clickhouse/clickhouse-server:24.9.1 + container_name: e2e.clickhouse + hostname: e2e.clickhouse + volumes: + - ./clickhouse/config.xml:/etc/clickhouse-server/config.xml + networks: + common: + aliases: + - "clickhouse_1" + expose: + - "2181" + - "9444" + - "9000" + e2e-clickhouse2: + image: clickhouse/clickhouse-server:24.9.1 + container_name: e2e.clickhouse2 + hostname: e2e.clickhouse2 + volumes: + - ./clickhouse/config2.xml:/etc/clickhouse-server/config.xml + networks: + common: + aliases: + - "clickhouse_2" + expose: + - "2181" + - "9444" + - "9000" + + +# CLUSTER_NAME=test_cluster_two_shards CLICKHOUSE_SERVER=e2e.clickhouse QRYN_LOGIN=a QRYN_PASSWORD=b BULK_MAX_AGE_MS=100 PORT=9080 \ No newline at end of file diff --git a/shared/commonroutes/controller.go b/shared/commonroutes/controller.go new file mode 100644 index 00000000..3d86499d --- /dev/null +++ b/shared/commonroutes/controller.go @@ -0,0 +1,33 @@ +package commonroutes + +import ( + "encoding/json" + "github.com/metrico/qryn/reader/watchdog" + "github.com/metrico/qryn/writer/utils/logger" + "net/http" +) + +func Ready(w http.ResponseWriter, r *http.Request) { + err := watchdog.Check() + if err != nil { + w.WriteHeader(500) + logger.Error(err.Error()) + w.Write([]byte("Internal Server Error")) + return + } + w.WriteHeader(200) + w.Write([]byte("OK")) +} + +func Config(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte("Not supported")) +} + +func BuildInfo(w http.ResponseWriter, r *http.Request) { + r.Header.Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]string{ + "version": "0.0.1", //TODO: Replace with actual version + "branch": "main", + }) +} diff --git a/shared/commonroutes/routes.go b/shared/commonroutes/routes.go new file mode 100644 index 00000000..70444d48 --- /dev/null +++ b/shared/commonroutes/routes.go @@ -0,0 +1,20 @@ +package commonroutes + +import ( + "github.com/gorilla/mux" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +// RegisterCommonRoutes registers the common routes to the given mux. +func RegisterCommonRoutes(app *mux.Router) { + app.HandleFunc("/ready", Ready).Methods("GET") + app.HandleFunc("/config", Config).Methods("GET") + app.Handle("/metrics", promhttp.InstrumentMetricHandler( + prometheus.DefaultRegisterer, + promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{ + DisableCompression: true, + }), + )).Methods("GET") + app.HandleFunc("/api/status/buildinfo", BuildInfo).Methods("GET") +} diff --git a/test/__snapshots__/parser.test.js.snap b/test/__snapshots__/parser.test.js.snap deleted file mode 100644 index 9d5fa0f5..00000000 --- a/test/__snapshots__/parser.test.js.snap +++ /dev/null @@ -1,217 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should compile regex 1`] = ` -"SCRIPT: abcd\\( - SYNTAX: abcd\\( - literal: a - letter: a - literal: b - letter: b - literal: c - letter: c - literal: d - letter: d - literal: \\( - quoted_brack: \\( -" -`; - -exports[`should compile regex 2`] = ` -"SCRIPT: (a\\(bc) - SYNTAX: (a\\(bc) - any_group: (a\\(bc) - group_tail: a\\(bc - literal: a - letter: a - literal: \\( - quoted_brack: \\( - literal: b - letter: b - literal: c - letter: c -" -`; - -exports[`should compile regex 3`] = ` -"SCRIPT: (?<label1>a[^\\[\\(\\)]bc) - SYNTAX: (?<label1>a[^\\[\\(\\)]bc) - any_group: (?<label1>a[^\\[\\(\\)]bc) - group_name: ?<label1> - label: label1 - group_tail: a[^\\[\\(\\)]bc - literal: a - letter: a - literal: [ - letter: [ - literal: ^ - letter: ^ - literal: \\ - letter: \\ - literal: [ - letter: [ - literal: \\( - quoted_brack: \\( - literal: \\) - quoted_brack: \\) - literal: ] - letter: ] - literal: b - letter: b - literal: c - letter: c -" -`; - -exports[`should compile regex 4`] = ` -"SCRIPT: (a(?<label1>[^\\[\\(\\)]bc)) - SYNTAX: (a(?<label1>[^\\[\\(\\)]bc)) - any_group: (a(?<label1>[^\\[\\(\\)]bc)) - group_tail: a(?<label1>[^\\[\\(\\)]bc) - literal: a - letter: a - any_group: (?<label1>[^\\[\\(\\)]bc) - group_name: ?<label1> - label: label1 - group_tail: [^\\[\\(\\)]bc - literal: [ - letter: [ - literal: ^ - letter: ^ - literal: \\ - letter: \\ - literal: [ - letter: [ - literal: \\( - quoted_brack: \\( - literal: \\) - quoted_brack: \\) - literal: ] - letter: ] - literal: b - letter: b - literal: c - letter: c -" -`; - -exports[`should compile regex 5`] = ` -"SCRIPT: (a[\\(\\)]+(?<l2>b)(?<label1>[^\\[\\(\\)]bc)) - SYNTAX: (a[\\(\\)]+(?<l2>b)(?<label1>[^\\[\\(\\)]bc)) - any_group: (a[\\(\\)]+(?<l2>b)(?<label1>[^\\[\\(\\)]bc)) - group_tail: a[\\(\\)]+(?<l2>b)(?<label1>[^\\[\\(\\)]bc) - literal: a - letter: a - literal: [ - letter: [ - literal: \\( - quoted_brack: \\( - literal: \\) - quoted_brack: \\) - literal: ] - letter: ] - literal: + - letter: + - any_group: (?<l2>b) - group_name: ?<l2> - label: l2 - group_tail: b - literal: b - letter: b - any_group: (?<label1>[^\\[\\(\\)]bc) - group_name: ?<label1> - label: label1 - group_tail: [^\\[\\(\\)]bc - literal: [ - letter: [ - literal: ^ - letter: ^ - literal: \\ - letter: \\ - literal: [ - letter: [ - literal: \\( - quoted_brack: \\( - literal: \\) - quoted_brack: \\) - literal: ] - letter: ] - literal: b - letter: b - literal: c - letter: c -" -`; - -exports[`should erase names 1`] = `"abcd\\("`; - -exports[`should erase names 2`] = `"(a\\(bc)"`; - -exports[`should erase names 3`] = `"(a[^\\[\\(\\)]bc)"`; - -exports[`should erase names 4`] = `"(a([^\\[\\(\\)]bc))"`; - -exports[`should erase names 5`] = `"(a[\\(\\)]+(b)([^\\[\\(\\)]bc))"`; - -exports[`should get named groups 1`] = `[]`; - -exports[`should get named groups 2`] = ` -[ - { - "val": "a\\(bc", - }, -] -`; - -exports[`should get named groups 3`] = ` -[ - { - "name": "label1", - "val": "a[^\\[\\(\\)]bc", - }, -] -`; - -exports[`should get named groups 4`] = ` -[ - { - "val": "a(?<label1>[^\\[\\(\\)]bc)", - }, - { - "name": "label1", - "val": "[^\\[\\(\\)]bc", - }, -] -`; - -exports[`should get named groups 5`] = ` -[ - { - "val": "a[\\(\\)]+(?<l2>b)(?<label1>[^\\[\\(\\)]bc)", - }, - { - "name": "l2", - "val": "b", - }, - { - "name": "label1", - "val": "[^\\[\\(\\)]bc", - }, -] -`; - -exports[`should process regex 1`] = ` -{ - "labels": [ - { - "name": "helper", - "val": "[a-zA-Z0-9]+", - }, - { - "name": "token", - "val": "[a-zA-Z]+", - }, - ], - "re": "([a-zA-Z0-9]+).. -.([a-zA-Z]+)", -} -`; diff --git a/test/__snapshots__/transpiler.test.js.snap b/test/__snapshots__/transpiler.test.js.snap deleted file mode 100644 index 94a26ba0..00000000 --- a/test/__snapshots__/transpiler.test.js.snap +++ /dev/null @@ -1,13034 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`log_range_aggregation 1 1`] = ` -Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": { - "duration": 300000, - "idxId": 1, - "matrix": true, - "step": 5000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "labels", - "asc", - ], - [ - "timestamp_ns", - "asc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - Raw { - "raw": "labels", - }, - "labels", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "toFloat64(count(1)) * 1000 / 300000", - }, - "value", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "minus_nam", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "aut illo", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "duration": 300000, - "idxId": 1, - "matrix": true, - "step": 5000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": { - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "minus_nam", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "aut illo", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - "rate_a": With { - "alias": "rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "minus_nam", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "aut illo", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "duration": 300000, - "idxId": 1, - "matrix": true, - "step": 5000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`log_range_aggregation 1 2`] = `"WITH idx_sel AS (select \`sel_1\`.\`fingerprint\` from (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'minus_nam') and (\`val\` = 'aut illo'))) as \`sel_1\`), rate_a AS (select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\` from loki.samples_vX as \`samples\` where ((\`samples\`.\`timestamp_ns\` between 1 and 2) and (\`samples\`.\`type\` in (0,0))) and (samples.fingerprint IN idx_sel) order by \`timestamp_ns\` desc) select labels as \`labels\`,intDiv(timestamp_ns, 300000) * 300000 as \`timestamp_ns\`,toFloat64(count(1)) * 1000 / 300000 as \`value\` from rate_a group by \`labels\`,\`timestamp_ns\` order by \`labels\` asc,\`timestamp_ns\` asc"`; - -exports[`log_range_aggregation 2 1`] = ` -Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": { - "duration": 300000, - "idxId": 1, - "matrix": true, - "step": 5000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "labels", - "asc", - ], - [ - "timestamp_ns", - "asc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - Raw { - "raw": "labels", - }, - "labels", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "toFloat64(count(1)) * 1000 / 300000", - }, - "value", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "rerum_laborum", - }, - }, - Condition { - "column": Match { - "col": "val", - "raw": "", - "re": Value { - "value": "^con.+q.at[a-z]r", - }, - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "notLike(string, '%consequatur nam soluta%')", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "ctx": { - "duration": 300000, - "idxId": 1, - "matrix": true, - "step": 5000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": { - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "rerum_laborum", - }, - }, - Condition { - "column": Match { - "col": "val", - "raw": "", - "re": Value { - "value": "^con.+q.at[a-z]r", - }, - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - "rate_a": With { - "alias": "rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "rerum_laborum", - }, - }, - Condition { - "column": Match { - "col": "val", - "raw": "", - "re": Value { - "value": "^con.+q.at[a-z]r", - }, - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "notLike(string, '%consequatur nam soluta%')", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "ctx": { - "duration": 300000, - "idxId": 1, - "matrix": true, - "step": 5000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`log_range_aggregation 2 2`] = `"WITH idx_sel AS (select \`sel_1\`.\`fingerprint\` from (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'rerum_laborum') and (match(val, '^con.+q.at[a-z]r') = 1))) as \`sel_1\`), rate_a AS (select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\` from loki.samples_vX as \`samples\` where ((\`samples\`.\`timestamp_ns\` between 1 and 2) and (\`samples\`.\`type\` in (0,0))) and (samples.fingerprint IN idx_sel) and (notLike(string, '%consequatur nam soluta%') = 1) order by \`timestamp_ns\` desc) select labels as \`labels\`,intDiv(timestamp_ns, 300000) * 300000 as \`timestamp_ns\`,toFloat64(count(1)) * 1000 / 300000 as \`value\` from rate_a group by \`labels\`,\`timestamp_ns\` order by \`labels\` asc,\`timestamp_ns\` asc"`; - -exports[`log_range_aggregation 3 1`] = ` -Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": { - "duration": 300000, - "idxId": 1, - "matrix": true, - "step": 5000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "labels", - "asc", - ], - [ - "timestamp_ns", - "asc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - Raw { - "raw": "labels", - }, - "labels", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "toFloat64(count(1)) * 1000 / 300000", - }, - "value", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "et_dolorem", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "!=", - "value": Value { - "value": "nemo doloremque", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "match(string, '^mol[eE][^ ]+e +voluptatibus')", - }, - "operator": "=", - "value": Raw { - "raw": "1", - }, - }, - ], - }, - "ctx": { - "duration": 300000, - "idxId": 1, - "matrix": true, - "step": 5000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": { - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "et_dolorem", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "!=", - "value": Value { - "value": "nemo doloremque", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - "rate_a": With { - "alias": "rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "et_dolorem", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "!=", - "value": Value { - "value": "nemo doloremque", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "match(string, '^mol[eE][^ ]+e +voluptatibus')", - }, - "operator": "=", - "value": Raw { - "raw": "1", - }, - }, - ], - }, - "ctx": { - "duration": 300000, - "idxId": 1, - "matrix": true, - "step": 5000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`log_range_aggregation 3 2`] = `"WITH idx_sel AS (select \`sel_1\`.\`fingerprint\` from (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'et_dolorem') and (\`val\` != 'nemo doloremque'))) as \`sel_1\`), rate_a AS (select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\` from loki.samples_vX as \`samples\` where ((\`samples\`.\`timestamp_ns\` between 1 and 2) and (\`samples\`.\`type\` in (0,0))) and (samples.fingerprint IN idx_sel) and (match(string, '^mol[eE][^ ]+e +voluptatibus') = 1) order by \`timestamp_ns\` desc) select labels as \`labels\`,intDiv(timestamp_ns, 300000) * 300000 as \`timestamp_ns\`,toFloat64(count(1)) * 1000 / 300000 as \`value\` from rate_a group by \`labels\`,\`timestamp_ns\` order by \`labels\` asc,\`timestamp_ns\` asc"`; - -exports[`log_range_aggregation 4 1`] = ` -Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": { - "duration": 1000, - "idxId": 1, - "matrix": true, - "step": 5000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "labels", - "asc", - ], - [ - "timestamp_ns", - "asc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "labels", - [ - Raw { - "raw": "intDiv(timestamp_ns, 5000) * 5000", - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "argMin(rate_b.value, rate_b.timestamp_ns)", - }, - "value", - ], - ], - "tables": [ - [ - Term { - "term": "rate_b", - }, - ], - ], - "withs": { - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "rerum_laborum", - }, - }, - Condition { - "column": Match { - "col": "val", - "raw": "", - "re": Value { - "value": "^con.+q.at[a-z]r", - }, - }, - "operator": "=", - "value": Value { - "value": 0, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - "rate_a": With { - "alias": "rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "rerum_laborum", - }, - }, - Condition { - "column": Match { - "col": "val", - "raw": "", - "re": Value { - "value": "^con.+q.at[a-z]r", - }, - }, - "operator": "=", - "value": Value { - "value": 0, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "match(string, 'cons[eE][^ ]+r nam soluta')", - }, - "operator": "=", - "value": Raw { - "raw": "0", - }, - }, - ], - }, - "ctx": { - "duration": 1000, - "idxId": 1, - "matrix": true, - "step": 5000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - "rate_b": With { - "alias": "rate_b", - "inline": undefined, - "query": Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "labels", - "asc", - ], - [ - "timestamp_ns", - "asc", - ], - ], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - Raw { - "raw": "labels", - }, - "labels", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "toFloat64(count(1)) * 1000 / 1000", - }, - "value", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "rerum_laborum", - }, - }, - Condition { - "column": Match { - "col": "val", - "raw": "", - "re": Value { - "value": "^con.+q.at[a-z]r", - }, - }, - "operator": "=", - "value": Value { - "value": 0, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "match(string, 'cons[eE][^ ]+r nam soluta')", - }, - "operator": "=", - "value": Raw { - "raw": "0", - }, - }, - ], - }, - "ctx": { - "duration": 1000, - "idxId": 1, - "matrix": true, - "step": 5000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`log_range_aggregation 4 2`] = `"WITH idx_sel AS (select \`sel_1\`.\`fingerprint\` from (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'rerum_laborum') and (match(val, '^con.+q.at[a-z]r') = 0))) as \`sel_1\`), rate_a AS (select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\` from loki.samples_vX as \`samples\` where ((\`samples\`.\`timestamp_ns\` between 1 and 2) and (\`samples\`.\`type\` in (0,0))) and (samples.fingerprint IN idx_sel) and (match(string, 'cons[eE][^ ]+r nam soluta') = 0) order by \`timestamp_ns\` desc), rate_b AS (select labels as \`labels\`,intDiv(timestamp_ns, 1000) * 1000 as \`timestamp_ns\`,toFloat64(count(1)) * 1000 / 1000 as \`value\` from rate_a group by \`labels\`,\`timestamp_ns\` order by \`labels\` asc,\`timestamp_ns\` asc) select \`labels\`,intDiv(timestamp_ns, 5000) * 5000 as \`timestamp_ns\`,argMin(rate_b.value, rate_b.timestamp_ns) as \`value\` from \`rate_b\` group by \`labels\`,\`timestamp_ns\` order by \`labels\` asc,\`timestamp_ns\` asc"`; - -exports[`shoud transpile unwrap 1`] = ` -Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": { - "duration": 60000, - "idxId": 1, - "matrix": true, - "step": 120000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "labels", - "asc", - ], - [ - "timestamp_ns", - "asc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": null, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 2000, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": null, - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": null, - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": null, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "labels", - [ - Raw { - "raw": "intDiv(timestamp_ns, 120000) * 120000", - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "argMin(uw_rate_b.value, uw_rate_b.timestamp_ns)", - }, - "value", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "uw_rate_b", - "inline": undefined, - "query": Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - "labels", - "timestamp_ns", - ], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - Raw { - "raw": "arraySort(arrayFilter(x -> arrayExists(y -> x.1 == y, ['fmt']) != 0, labels))", - }, - "labels", - ], - [ - Raw { - "raw": "SUM(unwrapped) / 60", - }, - "value", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "uw_rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "test_id", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "0.7857680014573265_json", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Raw { - "raw": "arrayExists(x -> x.1 == 'int_lbl', labels)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - Condition { - "column": Raw { - "raw": "isNotNull(unwrapped)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "duration": 60000, - "idxId": 1, - "matrix": true, - "step": 120000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": null, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 2000, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": null, - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": null, - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": null, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "toFloat64OrNull(arrayFirst(x -> x.1 == 'int_lbl', labels).2)", - }, - "unwrapped", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": null, - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": { - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "test_id", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "0.7857680014573265_json", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - "uw_rate_a": With { - "alias": "uw_rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "test_id", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "0.7857680014573265_json", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Raw { - "raw": "arrayExists(x -> x.1 == 'int_lbl', labels)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - Condition { - "column": Raw { - "raw": "isNotNull(unwrapped)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "duration": 60000, - "idxId": 1, - "matrix": true, - "step": 120000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": null, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 2000, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": null, - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": null, - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": null, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "toFloat64OrNull(arrayFirst(x -> x.1 == 'int_lbl', labels).2)", - }, - "unwrapped", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": null, - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - "uw_rate_b": With { - "alias": "uw_rate_b", - "inline": undefined, - "query": Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - "labels", - "timestamp_ns", - ], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - Raw { - "raw": "arraySort(arrayFilter(x -> arrayExists(y -> x.1 == y, ['fmt']) != 0, labels))", - }, - "labels", - ], - [ - Raw { - "raw": "SUM(unwrapped) / 60", - }, - "value", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "uw_rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "test_id", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "0.7857680014573265_json", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Raw { - "raw": "arrayExists(x -> x.1 == 'int_lbl', labels)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - Condition { - "column": Raw { - "raw": "isNotNull(unwrapped)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "duration": 60000, - "idxId": 1, - "matrix": true, - "step": 120000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": null, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 2000, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": null, - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": null, - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": null, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "toFloat64OrNull(arrayFirst(x -> x.1 == 'int_lbl', labels).2)", - }, - "unwrapped", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": null, - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`shoud transpile unwrap 2`] = ` -Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": { - "duration": 60000, - "idxId": 1, - "matrix": true, - "step": 120000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "labels", - "asc", - ], - [ - "timestamp_ns", - "asc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": null, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 2000, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": null, - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": null, - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": null, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "labels", - [ - Raw { - "raw": "intDiv(timestamp_ns, 120000) * 120000", - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "argMin(uw_rate_b.value, uw_rate_b.timestamp_ns)", - }, - "value", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "uw_rate_b", - "inline": undefined, - "query": Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - "labels", - "timestamp_ns", - ], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - Raw { - "raw": "arraySort(arrayFilter(x -> arrayExists(y -> x.1 == y, ['fmt']) != 0, arraySort(arrayConcat(arrayFilter(x -> arrayExists(y -> y.1 == x.1, extra_labels) == 0, labels), extra_labels))))", - }, - "labels", - ], - [ - Raw { - "raw": "SUM(unwrapped) / 60", - }, - "value", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "uw_rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "test_id", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "0.7857680014573265_json", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "isValidJSON(samples.string)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - Condition { - "column": Conjunction { - "args": [ - Disjunction { - "args": [ - Condition { - "column": Raw { - "raw": "arrayFirstIndex(x -> x.1 == 'int_lbl2', extra_labels)", - }, - "operator": "!=", - "value": Value { - "value": 0, - }, - }, - Condition { - "column": Raw { - "raw": "arrayExists(x -> x.1 == 'int_lbl2', labels)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - Condition { - "column": Raw { - "raw": "isNotNull(unwrapped)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "duration": 60000, - "idxId": 1, - "matrix": true, - "step": 120000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": null, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 2000, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": null, - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": null, - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": null, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "arrayFilter((x) -> x.2 != '', [('int_lbl2', if(JSONType(samples.string, 'int_val') == 'String', JSONExtractString(samples.string, 'int_val'), JSONExtractRaw(samples.string, 'int_val')))])", - }, - "extra_labels", - ], - [ - Raw { - "raw": "toFloat64OrNull(if(arrayExists(x -> x.1 == 'int_lbl2', extra_labels), arrayFirst(x -> x.1 == 'int_lbl2', extra_labels).2, arrayFirst(x -> x.1 == 'int_lbl2', labels).2))", - }, - "unwrapped", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": null, - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": { - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "test_id", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "0.7857680014573265_json", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - "uw_rate_a": With { - "alias": "uw_rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "test_id", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "0.7857680014573265_json", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "isValidJSON(samples.string)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - Condition { - "column": Conjunction { - "args": [ - Disjunction { - "args": [ - Condition { - "column": Raw { - "raw": "arrayFirstIndex(x -> x.1 == 'int_lbl2', extra_labels)", - }, - "operator": "!=", - "value": Value { - "value": 0, - }, - }, - Condition { - "column": Raw { - "raw": "arrayExists(x -> x.1 == 'int_lbl2', labels)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - Condition { - "column": Raw { - "raw": "isNotNull(unwrapped)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "duration": 60000, - "idxId": 1, - "matrix": true, - "step": 120000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": null, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 2000, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": null, - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": null, - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": null, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "arrayFilter((x) -> x.2 != '', [('int_lbl2', if(JSONType(samples.string, 'int_val') == 'String', JSONExtractString(samples.string, 'int_val'), JSONExtractRaw(samples.string, 'int_val')))])", - }, - "extra_labels", - ], - [ - Raw { - "raw": "toFloat64OrNull(if(arrayExists(x -> x.1 == 'int_lbl2', extra_labels), arrayFirst(x -> x.1 == 'int_lbl2', extra_labels).2, arrayFirst(x -> x.1 == 'int_lbl2', labels).2))", - }, - "unwrapped", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": null, - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - "uw_rate_b": With { - "alias": "uw_rate_b", - "inline": undefined, - "query": Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - "labels", - "timestamp_ns", - ], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - Raw { - "raw": "arraySort(arrayFilter(x -> arrayExists(y -> x.1 == y, ['fmt']) != 0, arraySort(arrayConcat(arrayFilter(x -> arrayExists(y -> y.1 == x.1, extra_labels) == 0, labels), extra_labels))))", - }, - "labels", - ], - [ - Raw { - "raw": "SUM(unwrapped) / 60", - }, - "value", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "uw_rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "test_id", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "0.7857680014573265_json", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "isValidJSON(samples.string)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - Condition { - "column": Conjunction { - "args": [ - Disjunction { - "args": [ - Condition { - "column": Raw { - "raw": "arrayFirstIndex(x -> x.1 == 'int_lbl2', extra_labels)", - }, - "operator": "!=", - "value": Value { - "value": 0, - }, - }, - Condition { - "column": Raw { - "raw": "arrayExists(x -> x.1 == 'int_lbl2', labels)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - Condition { - "column": Raw { - "raw": "isNotNull(unwrapped)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "duration": 60000, - "idxId": 1, - "matrix": true, - "step": 120000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": null, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 2000, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": null, - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": null, - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": null, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "arrayFilter((x) -> x.2 != '', [('int_lbl2', if(JSONType(samples.string, 'int_val') == 'String', JSONExtractString(samples.string, 'int_val'), JSONExtractRaw(samples.string, 'int_val')))])", - }, - "extra_labels", - ], - [ - Raw { - "raw": "toFloat64OrNull(if(arrayExists(x -> x.1 == 'int_lbl2', extra_labels), arrayFirst(x -> x.1 == 'int_lbl2', extra_labels).2, arrayFirst(x -> x.1 == 'int_lbl2', labels).2))", - }, - "unwrapped", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": null, - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`shoud transpile unwrap 3`] = ` -Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": { - "duration": 60000, - "idxId": 1, - "matrix": true, - "step": 120000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "labels", - "asc", - ], - [ - "timestamp_ns", - "asc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": null, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 2000, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": null, - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": null, - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": null, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "labels", - [ - Raw { - "raw": "intDiv(timestamp_ns, 120000) * 120000", - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "argMin(uw_rate_b.value, uw_rate_b.timestamp_ns)", - }, - "value", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "uw_rate_b", - "inline": undefined, - "query": Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - "labels", - "timestamp_ns", - ], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - Raw { - "raw": "arraySort(arrayFilter(x -> arrayExists(y -> x.1 == y, ['int_lbl2']) != 0, arraySort(arrayConcat(arrayFilter(x -> arrayExists(y -> y.1 == x.1, extra_labels) == 0, labels), extra_labels))))", - }, - "labels", - ], - [ - Raw { - "raw": "SUM(unwrapped) / 60", - }, - "value", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "uw_rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "test_id", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "0.7857680014573265_json", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "isValidJSON(samples.string)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - Condition { - "column": Conjunction { - "args": [ - Disjunction { - "args": [ - Condition { - "column": Raw { - "raw": "arrayFirstIndex(x -> x.1 == 'int_lbl', extra_labels)", - }, - "operator": "!=", - "value": Value { - "value": 0, - }, - }, - Condition { - "column": Raw { - "raw": "arrayExists(x -> x.1 == 'int_lbl', labels)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - Condition { - "column": Raw { - "raw": "isNotNull(unwrapped)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "duration": 60000, - "idxId": 1, - "matrix": true, - "step": 120000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": null, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 2000, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": null, - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": null, - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": null, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "arrayFilter((x) -> x.2 != '', [('int_lbl2', if(JSONType(samples.string, 'int_val') == 'String', JSONExtractString(samples.string, 'int_val'), JSONExtractRaw(samples.string, 'int_val')))])", - }, - "extra_labels", - ], - [ - Raw { - "raw": "toFloat64OrNull(if(arrayExists(x -> x.1 == 'int_lbl', extra_labels), arrayFirst(x -> x.1 == 'int_lbl', extra_labels).2, arrayFirst(x -> x.1 == 'int_lbl', labels).2))", - }, - "unwrapped", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": null, - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": { - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "test_id", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "0.7857680014573265_json", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - "uw_rate_a": With { - "alias": "uw_rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "test_id", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "0.7857680014573265_json", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "isValidJSON(samples.string)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - Condition { - "column": Conjunction { - "args": [ - Disjunction { - "args": [ - Condition { - "column": Raw { - "raw": "arrayFirstIndex(x -> x.1 == 'int_lbl', extra_labels)", - }, - "operator": "!=", - "value": Value { - "value": 0, - }, - }, - Condition { - "column": Raw { - "raw": "arrayExists(x -> x.1 == 'int_lbl', labels)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - Condition { - "column": Raw { - "raw": "isNotNull(unwrapped)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "duration": 60000, - "idxId": 1, - "matrix": true, - "step": 120000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": null, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 2000, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": null, - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": null, - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": null, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "arrayFilter((x) -> x.2 != '', [('int_lbl2', if(JSONType(samples.string, 'int_val') == 'String', JSONExtractString(samples.string, 'int_val'), JSONExtractRaw(samples.string, 'int_val')))])", - }, - "extra_labels", - ], - [ - Raw { - "raw": "toFloat64OrNull(if(arrayExists(x -> x.1 == 'int_lbl', extra_labels), arrayFirst(x -> x.1 == 'int_lbl', extra_labels).2, arrayFirst(x -> x.1 == 'int_lbl', labels).2))", - }, - "unwrapped", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": null, - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - "uw_rate_b": With { - "alias": "uw_rate_b", - "inline": undefined, - "query": Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - "labels", - "timestamp_ns", - ], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - Raw { - "raw": "arraySort(arrayFilter(x -> arrayExists(y -> x.1 == y, ['int_lbl2']) != 0, arraySort(arrayConcat(arrayFilter(x -> arrayExists(y -> y.1 == x.1, extra_labels) == 0, labels), extra_labels))))", - }, - "labels", - ], - [ - Raw { - "raw": "SUM(unwrapped) / 60", - }, - "value", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "uw_rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "test_id", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "0.7857680014573265_json", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "isValidJSON(samples.string)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - Condition { - "column": Conjunction { - "args": [ - Disjunction { - "args": [ - Condition { - "column": Raw { - "raw": "arrayFirstIndex(x -> x.1 == 'int_lbl', extra_labels)", - }, - "operator": "!=", - "value": Value { - "value": 0, - }, - }, - Condition { - "column": Raw { - "raw": "arrayExists(x -> x.1 == 'int_lbl', labels)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - Condition { - "column": Raw { - "raw": "isNotNull(unwrapped)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "duration": 60000, - "idxId": 1, - "matrix": true, - "step": 120000, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": null, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 2000, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": null, - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": null, - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": null, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "arrayFilter((x) -> x.2 != '', [('int_lbl2', if(JSONType(samples.string, 'int_val') == 'String', JSONExtractString(samples.string, 'int_val'), JSONExtractRaw(samples.string, 'int_val')))])", - }, - "extra_labels", - ], - [ - Raw { - "raw": "toFloat64OrNull(if(arrayExists(x -> x.1 == 'int_lbl', extra_labels), arrayFirst(x -> x.1 == 'int_lbl', extra_labels).2, arrayFirst(x -> x.1 == 'int_lbl', labels).2))", - }, - "unwrapped", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": null, - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`should transpile aggregation_operator 1`] = ` -Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": { - "duration": 300000, - "end": 3600000, - "idxId": 1, - "matrix": true, - "start": 0, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - "labels", - "timestamp_ns", - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - Raw { - "raw": "arrayFilter(x -> x.1 IN ('label_1'), labels)", - }, - "labels", - ], - "timestamp_ns", - [ - Raw { - "raw": "sum(value)", - }, - "value", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "agg_a", - "inline": undefined, - "query": Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": { - "duration": 300000, - "end": 3600000, - "idxId": 1, - "matrix": true, - "start": 0, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "labels", - "asc", - ], - [ - "timestamp_ns", - "asc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "labels", - [ - Raw { - "raw": "intDiv(timestamp_ns, undefined) * undefined", - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "argMin(rate_b.value, rate_b.timestamp_ns)", - }, - "value", - ], - ], - "tables": [ - [ - Term { - "term": "rate_b", - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": { - "agg_a": With { - "alias": "agg_a", - "inline": undefined, - "query": Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": { - "duration": 300000, - "end": 3600000, - "idxId": 1, - "matrix": true, - "start": 0, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "labels", - "asc", - ], - [ - "timestamp_ns", - "asc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "labels", - [ - Raw { - "raw": "intDiv(timestamp_ns, undefined) * undefined", - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "argMin(rate_b.value, rate_b.timestamp_ns)", - }, - "value", - ], - ], - "tables": [ - [ - Term { - "term": "rate_b", - }, - ], - ], - "withs": {}, - }, - }, - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "minus_nam", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "aut illo", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - "rate_a": With { - "alias": "rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "minus_nam", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "aut illo", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "duration": 300000, - "end": 3600000, - "idxId": 1, - "matrix": true, - "start": 0, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - "rate_b": With { - "alias": "rate_b", - "inline": undefined, - "query": Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "labels", - "asc", - ], - [ - "timestamp_ns", - "asc", - ], - ], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - Raw { - "raw": "labels", - }, - "labels", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "toFloat64(count(1)) * 1000 / 300000", - }, - "value", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "minus_nam", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "aut illo", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "duration": 300000, - "end": 3600000, - "idxId": 1, - "matrix": true, - "start": 0, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`should transpile aggregation_operator 2`] = `"WITH idx_sel AS (select \`sel_1\`.\`fingerprint\` from (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'minus_nam') and (\`val\` = 'aut illo'))) as \`sel_1\`), rate_a AS (select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\` from loki.samples_vX as \`samples\` where ((\`samples\`.\`timestamp_ns\` between 1 and 2) and (\`samples\`.\`type\` in (0,0))) and (samples.fingerprint IN idx_sel) order by \`timestamp_ns\` desc), rate_b AS (select labels as \`labels\`,intDiv(timestamp_ns, 300000) * 300000 as \`timestamp_ns\`,toFloat64(count(1)) * 1000 / 300000 as \`value\` from rate_a group by \`labels\`,\`timestamp_ns\` order by \`labels\` asc,\`timestamp_ns\` asc), agg_a AS (select \`labels\`,intDiv(timestamp_ns, undefined) * undefined as \`timestamp_ns\`,argMin(rate_b.value, rate_b.timestamp_ns) as \`value\` from \`rate_b\` group by \`labels\`,\`timestamp_ns\` order by \`labels\` asc,\`timestamp_ns\` asc) select arrayFilter(x -> x.1 IN ('label_1'), labels) as \`labels\`,\`timestamp_ns\`,sum(value) as \`value\` from agg_a group by \`labels\`,\`timestamp_ns\` order by \`labels\`,\`timestamp_ns\`"`; - -exports[`should transpile aggregation_operator 3`] = ` -Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": { - "duration": 300000, - "end": 3600000, - "idxId": 2, - "matrix": true, - "start": 0, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - "labels", - "timestamp_ns", - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - Raw { - "raw": "arrayFilter(x -> x.1 IN ('label_1'), labels)", - }, - "labels", - ], - "timestamp_ns", - [ - Raw { - "raw": "sum(value)", - }, - "value", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "agg_a", - "inline": undefined, - "query": Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": { - "duration": 300000, - "end": 3600000, - "idxId": 2, - "matrix": true, - "start": 0, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "labels", - "asc", - ], - [ - "timestamp_ns", - "asc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "labels", - [ - Raw { - "raw": "intDiv(timestamp_ns, undefined) * undefined", - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "argMin(rate_b.value, rate_b.timestamp_ns)", - }, - "value", - ], - ], - "tables": [ - [ - Term { - "term": "rate_b", - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": { - "agg_a": With { - "alias": "agg_a", - "inline": undefined, - "query": Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": { - "duration": 300000, - "end": 3600000, - "idxId": 2, - "matrix": true, - "start": 0, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "labels", - "asc", - ], - [ - "timestamp_ns", - "asc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "labels", - [ - Raw { - "raw": "intDiv(timestamp_ns, undefined) * undefined", - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "argMin(rate_b.value, rate_b.timestamp_ns)", - }, - "value", - ], - ], - "tables": [ - [ - Term { - "term": "rate_b", - }, - ], - ], - "withs": {}, - }, - }, - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_2.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "rerum_laborum", - }, - }, - Condition { - "column": Match { - "col": "val", - "raw": "", - "re": Value { - "value": "^con.+q.at[a-z]r", - }, - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_2", - }, - ], - ], - "withs": {}, - }, - }, - "rate_a": With { - "alias": "rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "fmt": undefined, - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "minus_nam", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "aut illo", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_2.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "rerum_laborum", - }, - }, - Condition { - "column": Match { - "col": "val", - "raw": "", - "re": Value { - "value": "^con.+q.at[a-z]r", - }, - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_2", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "notLike(string, '%consequatur nam soluta%')", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "ctx": { - "duration": 300000, - "end": 3600000, - "idxId": 2, - "matrix": true, - "start": 0, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - "rate_b": With { - "alias": "rate_b", - "inline": undefined, - "query": Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "labels", - "asc", - ], - [ - "timestamp_ns", - "asc", - ], - ], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - Raw { - "raw": "labels", - }, - "labels", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "toFloat64(count(1)) * 1000 / 300000", - }, - "value", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "fmt": undefined, - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "minus_nam", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "aut illo", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_2.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "rerum_laborum", - }, - }, - Condition { - "column": Match { - "col": "val", - "raw": "", - "re": Value { - "value": "^con.+q.at[a-z]r", - }, - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_2", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "notLike(string, '%consequatur nam soluta%')", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "ctx": { - "duration": 300000, - "end": 3600000, - "idxId": 2, - "matrix": true, - "start": 0, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`should transpile aggregation_operator 4`] = `"WITH idx_sel AS (select \`sel_2\`.\`fingerprint\` from (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'rerum_laborum') and (match(val, '^con.+q.at[a-z]r') = 1))) as \`sel_2\`), rate_a AS (select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\` from loki.samples_vX as \`samples\` where ((\`samples\`.\`timestamp_ns\` between 1 and 2) and (\`samples\`.\`type\` in (0,0))) and (samples.fingerprint IN idx_sel) and (samples.fingerprint IN idx_sel) and (notLike(string, '%consequatur nam soluta%') = 1) order by \`timestamp_ns\` desc), rate_b AS (select labels as \`labels\`,intDiv(timestamp_ns, 300000) * 300000 as \`timestamp_ns\`,toFloat64(count(1)) * 1000 / 300000 as \`value\` from rate_a group by \`labels\`,\`timestamp_ns\` order by \`labels\` asc,\`timestamp_ns\` asc), agg_a AS (select \`labels\`,intDiv(timestamp_ns, undefined) * undefined as \`timestamp_ns\`,argMin(rate_b.value, rate_b.timestamp_ns) as \`value\` from \`rate_b\` group by \`labels\`,\`timestamp_ns\` order by \`labels\` asc,\`timestamp_ns\` asc) select arrayFilter(x -> x.1 IN ('label_1'), labels) as \`labels\`,\`timestamp_ns\`,sum(value) as \`value\` from agg_a group by \`labels\`,\`timestamp_ns\` order by \`labels\`,\`timestamp_ns\`"`; - -exports[`should transpile aggregation_operator 5`] = ` -Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": { - "duration": 300000, - "end": 3600000, - "idxId": 1, - "matrix": true, - "start": 0, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - "labels", - "timestamp_ns", - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - Raw { - "raw": "arrayFilter(x -> x.1 IN ('label_1'), labels)", - }, - "labels", - ], - "timestamp_ns", - [ - Raw { - "raw": "sum(value)", - }, - "value", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "agg_a", - "inline": undefined, - "query": Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": { - "duration": 300000, - "end": 3600000, - "idxId": 1, - "matrix": true, - "start": 0, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "labels", - "asc", - ], - [ - "timestamp_ns", - "asc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "labels", - [ - Raw { - "raw": "intDiv(timestamp_ns, undefined) * undefined", - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "argMin(rate_b.value, rate_b.timestamp_ns)", - }, - "value", - ], - ], - "tables": [ - [ - Term { - "term": "rate_b", - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": { - "agg_a": With { - "alias": "agg_a", - "inline": undefined, - "query": Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": { - "duration": 300000, - "end": 3600000, - "idxId": 1, - "matrix": true, - "start": 0, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "labels", - "asc", - ], - [ - "timestamp_ns", - "asc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "labels", - [ - Raw { - "raw": "intDiv(timestamp_ns, undefined) * undefined", - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "argMin(rate_b.value, rate_b.timestamp_ns)", - }, - "value", - ], - ], - "tables": [ - [ - Term { - "term": "rate_b", - }, - ], - ], - "withs": {}, - }, - }, - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "minus_nam", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "aut illo", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - "rate_a": With { - "alias": "rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "minus_nam", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "aut illo", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "duration": 300000, - "end": 3600000, - "idxId": 1, - "matrix": true, - "start": 0, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - "rate_b": With { - "alias": "rate_b", - "inline": undefined, - "query": Select { - "aggregations": [ - "labels", - "timestamp_ns", - ], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "labels", - "asc", - ], - [ - "timestamp_ns", - "asc", - ], - ], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - Raw { - "raw": "labels", - }, - "labels", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "toFloat64(count(1)) * 1000 / 300000", - }, - "value", - ], - ], - "tables": [ - [ - WithReference { - "ref": With { - "alias": "rate_a", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "minus_nam", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "aut illo", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "duration": 300000, - "end": 3600000, - "idxId": 1, - "matrix": true, - "start": 0, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "timestamp_shift": Parameter { - "name": "timestamp_shift", - "value": null, - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": {}, - }, - }, - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`should transpile aggregation_operator 6`] = `"WITH idx_sel AS (select \`sel_1\`.\`fingerprint\` from (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'minus_nam') and (\`val\` = 'aut illo'))) as \`sel_1\`), rate_a AS (select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\` from loki.samples_vX as \`samples\` where ((\`samples\`.\`timestamp_ns\` between 1 and 2) and (\`samples\`.\`type\` in (0,0))) and (samples.fingerprint IN idx_sel) order by \`timestamp_ns\` desc), rate_b AS (select labels as \`labels\`,intDiv(timestamp_ns, 300000) * 300000 as \`timestamp_ns\`,toFloat64(count(1)) * 1000 / 300000 as \`value\` from rate_a group by \`labels\`,\`timestamp_ns\` order by \`labels\` asc,\`timestamp_ns\` asc), agg_a AS (select \`labels\`,intDiv(timestamp_ns, undefined) * undefined as \`timestamp_ns\`,argMin(rate_b.value, rate_b.timestamp_ns) as \`value\` from \`rate_b\` group by \`labels\`,\`timestamp_ns\` order by \`labels\` asc,\`timestamp_ns\` asc) select arrayFilter(x -> x.1 IN ('label_1'), labels) as \`labels\`,\`timestamp_ns\`,sum(value) as \`value\` from agg_a group by \`labels\`,\`timestamp_ns\` order by \`labels\`,\`timestamp_ns\`"`; - -exports[`should transpile complex pipelines 1`] = ` -{ - "duration": 1000, - "matrix": false, - "query": "WITH str_sel AS (select DISTINCT \`fingerprint\` from loki.time_series where ((JSONHas(labels, 'test_id') = 1) and (JSONExtractString(labels, 'test_id') = '\${testID}')) and ((JSONHas(labels, 'freq') = 1) and (toFloat64OrNull(JSONExtractString(labels, 'freq')) >= '4'))), sel_a AS (select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\`,JSONExtractKeysAndValues(time_series.labels, 'String') as \`labels\` from loki.samples_vX as \`samples\` left any join (select \`fingerprint\`,\`labels\` from \`loki\`.\`time_series\` as \`time_series\` where ((\`time_series\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (date >= toDate(fromUnixTimestamp(intDiv(0000000, 1000000000)))) and (date <= toDate(fromUnixTimestamp(intDiv(100000000000000, 1000000000)))))) AS time_series on \`samples\`.\`fingerprint\` = time_series.fingerprint where ((\`samples\`.\`timestamp_ns\` between 0000000 and 100000000000000) and (\`samples\`.\`type\` in (0,0))) and (\`samples\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) order by \`timestamp_ns\` asc limit 1000) select * from sel_a order by \`labels\` asc,\`timestamp_ns\` asc", - "stream": [], -} -`; - -exports[`should transpile json requests 1`] = ` -Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "autem_quis", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "quidem sit", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "isValidJSON(samples.string)", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "ctx": { - "idxId": 1, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": { - "number": Parameter { - "name": "limit", - "value": 3, - }, - "offset": undefined, - }, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - [ - Raw { - "raw": "arrayFilter((x) -> x.2 != '', [('odit_iusto', if(JSONType(samples.string, 'dicta') == 'String', JSONExtractString(samples.string, 'dicta'), JSONExtractRaw(samples.string, 'dicta')))])", - }, - "extra_labels", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": { - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "autem_quis", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "quidem sit", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`should transpile json requests 2`] = ` -[ - { - "labels": { - "autem_quis": "quidem sit", - "l1": "v3", - "l2": "v2", - "l3": "v4", - }, - "string": "{"l1":"v3","l3":"v4"}", - }, -] -`; - -exports[`should transpile line format 1`] = ` -[ - { - "labels": { - "int": 10, - "lbl1": "a", - }, - "string": "str a 5", - }, -] -`; - -exports[`should transpile line format 2`] = ` -[ - { - "labels": { - "entry": "str", - "int": 10, - "intval": "5", - "lbl1": "a", - }, - "string": "{ "entry": "str", "intval": 5 }", - }, -] -`; - -exports[`should transpile line format 3`] = ` -[ - { - "labels": { - "entry": "str", - "int": 10, - "intval": "5", - "lbl1": "a", - }, - "timestamp_ns": "0", - "value": 5, - }, - { - "EOF": true, - }, -] -`; - -exports[`should transpile log_stream_selector 1`] = ` -Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [ - { - "conditions": [ - Condition { - "column": Term { - "term": "sel_1.fingerprint", - }, - "operator": "=", - "value": Term { - "term": "sel_2.fingerprint", - }, - }, - ], - "table": AliasedSelect { - "alias": Term { - "term": "sel_2", - }, - "sel": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "quia", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "eum voluptatem non eligendi", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - "type": " inner any ", - }, - ], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "et_dolorem", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "nemo doloremque", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "idxId": 2, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": { - "number": Parameter { - "name": "limit", - "value": 3, - }, - "offset": undefined, - }, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": { - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [ - { - "conditions": [ - Condition { - "column": Term { - "term": "sel_1.fingerprint", - }, - "operator": "=", - "value": Term { - "term": "sel_2.fingerprint", - }, - }, - ], - "table": AliasedSelect { - "alias": Term { - "term": "sel_2", - }, - "sel": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "quia", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "eum voluptatem non eligendi", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - "type": " inner any ", - }, - ], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "et_dolorem", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "nemo doloremque", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`should transpile log_stream_selector 2`] = `"WITH idx_sel AS (select \`sel_1\`.\`fingerprint\` from (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'et_dolorem') and (\`val\` = 'nemo doloremque'))) as \`sel_1\` inner any join (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'quia') and (\`val\` = 'eum voluptatem non eligendi'))) as \`sel_2\` on \`sel_1\`.\`fingerprint\` = \`sel_2\`.\`fingerprint\`) select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\` from loki.samples_vX as \`samples\` where ((\`samples\`.\`timestamp_ns\` between 1 and 2) and (\`samples\`.\`type\` in (0,0))) and (samples.fingerprint IN idx_sel) order by \`timestamp_ns\` desc limit 3"`; - -exports[`should transpile log_stream_selector 3`] = ` -Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "rerum_laborum", - }, - }, - Condition { - "column": Match { - "col": "val", - "raw": "", - "re": Value { - "value": "^con.+q.at[a-z]r", - }, - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "idxId": 1, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": { - "number": Parameter { - "name": "limit", - "value": 3, - }, - "offset": undefined, - }, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": { - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "rerum_laborum", - }, - }, - Condition { - "column": Match { - "col": "val", - "raw": "", - "re": Value { - "value": "^con.+q.at[a-z]r", - }, - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`should transpile log_stream_selector 4`] = `"WITH idx_sel AS (select \`sel_1\`.\`fingerprint\` from (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'rerum_laborum') and (match(val, '^con.+q.at[a-z]r') = 1))) as \`sel_1\`) select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\` from loki.samples_vX as \`samples\` where ((\`samples\`.\`timestamp_ns\` between 1 and 2) and (\`samples\`.\`type\` in (0,0))) and (samples.fingerprint IN idx_sel) order by \`timestamp_ns\` desc limit 3"`; - -exports[`should transpile log_stream_selector 5`] = ` -Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "et_dolorem", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "!=", - "value": Value { - "value": "nemo doloremque", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "idxId": 1, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": { - "number": Parameter { - "name": "limit", - "value": 3, - }, - "offset": undefined, - }, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": { - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "et_dolorem", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "!=", - "value": Value { - "value": "nemo doloremque", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`should transpile log_stream_selector 6`] = `"WITH idx_sel AS (select \`sel_1\`.\`fingerprint\` from (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'et_dolorem') and (\`val\` != 'nemo doloremque'))) as \`sel_1\`) select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\` from loki.samples_vX as \`samples\` where ((\`samples\`.\`timestamp_ns\` between 1 and 2) and (\`samples\`.\`type\` in (0,0))) and (samples.fingerprint IN idx_sel) order by \`timestamp_ns\` desc limit 3"`; - -exports[`should transpile log_stream_selector 7`] = ` -Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "rerum_laborum", - }, - }, - Condition { - "column": Match { - "col": "val", - "raw": "", - "re": Value { - "value": "^con.+q.at[a-z]r", - }, - }, - "operator": "=", - "value": Value { - "value": 0, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": { - "idxId": 1, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": { - "number": Parameter { - "name": "limit", - "value": 3, - }, - "offset": undefined, - }, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": { - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "rerum_laborum", - }, - }, - Condition { - "column": Match { - "col": "val", - "raw": "", - "re": Value { - "value": "^con.+q.at[a-z]r", - }, - }, - "operator": "=", - "value": Value { - "value": 0, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`should transpile log_stream_selector 8`] = `"WITH idx_sel AS (select \`sel_1\`.\`fingerprint\` from (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'rerum_laborum') and (match(val, '^con.+q.at[a-z]r') = 0))) as \`sel_1\`) select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\` from loki.samples_vX as \`samples\` where ((\`samples\`.\`timestamp_ns\` between 1 and 2) and (\`samples\`.\`type\` in (0,0))) and (samples.fingerprint IN idx_sel) order by \`timestamp_ns\` desc limit 3"`; - -exports[`should transpile log_stream_selector with stream filter 1`] = ` -Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [ - { - "conditions": [ - Condition { - "column": Term { - "term": "sel_1.fingerprint", - }, - "operator": "=", - "value": Term { - "term": "sel_2.fingerprint", - }, - }, - ], - "table": AliasedSelect { - "alias": Term { - "term": "sel_2", - }, - "sel": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "quia", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "eum voluptatem non eligendi", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - "type": " inner any ", - }, - ], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "et_dolorem", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "nemo doloremque", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "like(string, '%at et%')", - }, - "operator": "!=", - "value": Value { - "value": 0, - }, - }, - ], - }, - "ctx": { - "idxId": 2, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": { - "number": Parameter { - "name": "limit", - "value": 3, - }, - "offset": undefined, - }, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": { - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [ - { - "conditions": [ - Condition { - "column": Term { - "term": "sel_1.fingerprint", - }, - "operator": "=", - "value": Term { - "term": "sel_2.fingerprint", - }, - }, - ], - "table": AliasedSelect { - "alias": Term { - "term": "sel_2", - }, - "sel": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "quia", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "eum voluptatem non eligendi", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - "type": " inner any ", - }, - ], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "et_dolorem", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "=", - "value": Value { - "value": "nemo doloremque", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`should transpile log_stream_selector with stream filter 2`] = `"WITH idx_sel AS (select \`sel_1\`.\`fingerprint\` from (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'et_dolorem') and (\`val\` = 'nemo doloremque'))) as \`sel_1\` inner any join (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'quia') and (\`val\` = 'eum voluptatem non eligendi'))) as \`sel_2\` on \`sel_1\`.\`fingerprint\` = \`sel_2\`.\`fingerprint\`) select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\` from loki.samples_vX as \`samples\` where ((\`samples\`.\`timestamp_ns\` between 1 and 2) and (\`samples\`.\`type\` in (0,0))) and (samples.fingerprint IN idx_sel) and (like(string, '%at et%') != 0) order by \`timestamp_ns\` desc limit 3"`; - -exports[`should transpile log_stream_selector with stream filter 3`] = ` -Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "rerum_laborum", - }, - }, - Condition { - "column": Match { - "col": "val", - "raw": "", - "re": Value { - "value": "^con.+q.at[a-z]r", - }, - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "notLike(string, '%consequatur nam soluta%')", - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "ctx": { - "idxId": 1, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": { - "number": Parameter { - "name": "limit", - "value": 3, - }, - "offset": undefined, - }, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": { - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "rerum_laborum", - }, - }, - Condition { - "column": Match { - "col": "val", - "raw": "", - "re": Value { - "value": "^con.+q.at[a-z]r", - }, - }, - "operator": "=", - "value": Value { - "value": 1, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`should transpile log_stream_selector with stream filter 4`] = `"WITH idx_sel AS (select \`sel_1\`.\`fingerprint\` from (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'rerum_laborum') and (match(val, '^con.+q.at[a-z]r') = 1))) as \`sel_1\`) select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\` from loki.samples_vX as \`samples\` where ((\`samples\`.\`timestamp_ns\` between 1 and 2) and (\`samples\`.\`type\` in (0,0))) and (samples.fingerprint IN idx_sel) and (notLike(string, '%consequatur nam soluta%') = 1) order by \`timestamp_ns\` desc limit 3"`; - -exports[`should transpile log_stream_selector with stream filter 5`] = ` -Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "et_dolorem", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "!=", - "value": Value { - "value": "nemo doloremque", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "match(string, '^mol[eE][^ ]+e +voluptatibus')", - }, - "operator": "=", - "value": Raw { - "raw": "1", - }, - }, - ], - }, - "ctx": { - "idxId": 1, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": { - "number": Parameter { - "name": "limit", - "value": 3, - }, - "offset": undefined, - }, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": { - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "et_dolorem", - }, - }, - Condition { - "column": Term { - "term": "val", - }, - "operator": "!=", - "value": Value { - "value": "nemo doloremque", - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`should transpile log_stream_selector with stream filter 6`] = `"WITH idx_sel AS (select \`sel_1\`.\`fingerprint\` from (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'et_dolorem') and (\`val\` != 'nemo doloremque'))) as \`sel_1\`) select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\` from loki.samples_vX as \`samples\` where ((\`samples\`.\`timestamp_ns\` between 1 and 2) and (\`samples\`.\`type\` in (0,0))) and (samples.fingerprint IN idx_sel) and (match(string, '^mol[eE][^ ]+e +voluptatibus') = 1) order by \`timestamp_ns\` desc limit 3"`; - -exports[`should transpile log_stream_selector with stream filter 7`] = ` -Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Raw { - "raw": "", - "toString": [Function], - }, - In { - "column": Term { - "term": "samples.type", - }, - "operator": "in", - "value": Value { - "value": [ - 0, - 0, - ], - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": InSubreq { - "col": "samples.fingerprint", - "raw": undefined, - "sub": WithReference { - "ref": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "rerum_laborum", - }, - }, - Condition { - "column": Match { - "col": "val", - "raw": "", - "re": Value { - "value": "^con.+q.at[a-z]r", - }, - }, - "operator": "=", - "value": Value { - "value": 0, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - Condition { - "column": Raw { - "raw": "match(string, 'cons[eE][^ ]+r nam soluta')", - }, - "operator": "=", - "value": Raw { - "raw": "0", - }, - }, - ], - }, - "ctx": { - "idxId": 1, - }, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": { - "number": Parameter { - "name": "limit", - "value": 3, - }, - "offset": undefined, - }, - "order_expressions": [ - [ - "timestamp_ns", - "desc", - ], - ], - "params": { - "from": Parameter { - "name": "from", - "value": 1, - }, - "isMatrix": Parameter { - "name": "isMatrix", - "value": null, - }, - "limit": Parameter { - "name": "limit", - "value": 3, - }, - "samplesTable": Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - "timeSeriesTable": Parameter { - "name": "timeSeriesTable", - "value": "loki.time_series", - }, - "to": Parameter { - "name": "to", - "value": 2, - }, - }, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - [ - "samples.string", - "string", - ], - [ - "samples.fingerprint", - "fingerprint", - ], - [ - Raw { - "raw": "", - "toString": [Function], - }, - "timestamp_ns", - ], - ], - "tables": [ - [ - Parameter { - "name": "samplesTable", - "value": "loki.samples_vX", - }, - Term { - "term": "samples", - }, - ], - ], - "withs": { - "idx_sel": With { - "alias": "idx_sel", - "inline": undefined, - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "sel_1.fingerprint", - ], - "tables": [ - [ - Subquery { - "query": Select { - "aggregations": [], - "conditions": Conjunction { - "args": [ - Condition { - "column": Conjunction { - "args": [ - Condition { - "column": Term { - "term": "key", - }, - "operator": "=", - "value": Value { - "value": "rerum_laborum", - }, - }, - Condition { - "column": Match { - "col": "val", - "raw": "", - "re": Value { - "value": "^con.+q.at[a-z]r", - }, - }, - "operator": "=", - "value": Value { - "value": 0, - }, - }, - ], - }, - "operator": undefined, - "value": Value { - "value": undefined, - }, - }, - ], - }, - "ctx": {}, - "dist": false, - "fmt": undefined, - "having_conditions": Conjunction { - "args": [], - }, - "joins": [], - "limitbycolumns": undefined, - "limits": undefined, - "order_expressions": [], - "params": {}, - "preconditions": Conjunction { - "args": [], - }, - "request_totals": undefined, - "sampling": undefined, - "select_list": [ - "fingerprint", - ], - "tables": [ - [ - Term { - "term": "loki.time_series_gin", - }, - ], - ], - "withs": {}, - }, - }, - Term { - "term": "sel_1", - }, - ], - ], - "withs": {}, - }, - }, - }, -} -`; - -exports[`should transpile log_stream_selector with stream filter 8`] = `"WITH idx_sel AS (select \`sel_1\`.\`fingerprint\` from (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'rerum_laborum') and (match(val, '^con.+q.at[a-z]r') = 0))) as \`sel_1\`) select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\` from loki.samples_vX as \`samples\` where ((\`samples\`.\`timestamp_ns\` between 1 and 2) and (\`samples\`.\`type\` in (0,0))) and (samples.fingerprint IN idx_sel) and (match(string, 'cons[eE][^ ]+r nam soluta') = 0) order by \`timestamp_ns\` desc limit 3"`; - -exports[`should transpile logfmt requests 1`] = ` -[ - { - "labels": { - "autem_quis": "quidem sit", - "l1": "v3", - "l2": "v2", - "l3": "v4", - }, - "string": "l1="v3" l3="v4" ", - }, -] -`; - -exports[`should transpile new style 1 1`] = ` -{ - "duration": 1000, - "matrix": false, - "query": "WITH str_sel AS (select DISTINCT \`fingerprint\` from loki.time_series where ((JSONHas(labels, 'test_id') = 1) and (JSONExtractString(labels, 'test_id') = '0.7387779420506657'))), sel_a AS (select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\` from loki.samples_vX as \`samples\` where ((\`samples\`.\`timestamp_ns\` between 1638802620000000000 and 1638803220000000000) and (\`samples\`.\`type\` in (0,0))) and (\`samples\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) order by \`timestamp_ns\` desc limit 2000) select JSONExtractKeysAndValues(time_series.labels, 'String') as \`labels\`,samples.* from sel_a as \`samples\` left any join (select \`fingerprint\`,\`labels\` from \`loki\`.\`time_series\` as \`time_series\` where ((\`time_series\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (date >= toDate(fromUnixTimestamp(intDiv(1638802620000000000, 1000000000)))) and (date <= toDate(fromUnixTimestamp(intDiv(1638803220000000000, 1000000000)))))) AS time_series on \`samples\`.\`fingerprint\` = time_series.fingerprint order by \`labels\` desc,\`timestamp_ns\` desc", - "stream": [], -} -`; - -exports[`should transpile new style 2 1`] = ` -{ - "duration": 1000, - "matrix": false, - "query": "WITH str_sel AS (select DISTINCT \`fingerprint\` from loki.time_series where ((JSONHas(labels, 'test_id') = 1) and (JSONExtractString(labels, 'test_id') = '0.2119268970232')) and ((JSONHas(labels, 'freq') = 1) and (JSONExtractString(labels, 'freq') = '2'))), sel_a AS (select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\` from loki.samples_vX as \`samples\` where ((\`samples\`.\`timestamp_ns\` between 1638802620000000000 and 1638803220000000000) and (\`samples\`.\`type\` in (0,0))) and (\`samples\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (match(string, '2[0-9]$') = 1) order by \`timestamp_ns\` desc limit 2000) select JSONExtractKeysAndValues(time_series.labels, 'String') as \`labels\`,samples.* from sel_a as \`samples\` left any join (select \`fingerprint\`,\`labels\` from \`loki\`.\`time_series\` as \`time_series\` where ((\`time_series\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (date >= toDate(fromUnixTimestamp(intDiv(1638802620000000000, 1000000000)))) and (date <= toDate(fromUnixTimestamp(intDiv(1638803220000000000, 1000000000)))))) AS time_series on \`samples\`.\`fingerprint\` = time_series.fingerprint order by \`labels\` desc,\`timestamp_ns\` desc", - "stream": [], -} -`; - -exports[`should transpile new style 3 1`] = ` -{ - "duration": 1000, - "matrix": true, - "query": "WITH str_sel AS (select DISTINCT \`fingerprint\` from loki.time_series where ((JSONHas(labels, 'test_id') = 1) and (JSONExtractString(labels, 'test_id') = '0.7026038163617259')) and ((JSONHas(labels, 'freq') = 1) and (JSONExtractString(labels, 'freq') = '2'))), rate_a AS (select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,intDiv(samples.timestamp_ns, 1000000) as \`timestamp_ns\`,JSONExtractKeysAndValues(time_series.labels, 'String') as \`labels\` from loki.samples_vX as \`samples\` left any join (select \`fingerprint\`,\`labels\` from \`loki\`.\`time_series\` as \`time_series\` where ((\`time_series\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (date >= toDate(fromUnixTimestamp(intDiv(1638802620000000000, 1000000000)))) and (date <= toDate(fromUnixTimestamp(intDiv(1638803220000000000, 1000000000)))))) AS time_series on \`samples\`.\`fingerprint\` = time_series.fingerprint where ((\`samples\`.\`timestamp_ns\` between 1638802620000000000 and 1638803220000000000) and (\`samples\`.\`type\` in (0,0))) and (\`samples\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (match(string, '2[0-9]$') = 1) order by \`timestamp_ns\` desc), rate_b AS (select labels as \`labels\`,intDiv(timestamp_ns, 1000) * 1000 as \`timestamp_ns\`,toFloat64(count(1)) * 1000 / 1000 as \`value\` from rate_a group by \`labels\`,\`timestamp_ns\` order by \`labels\` asc,\`timestamp_ns\` asc) select \`labels\`,intDiv(timestamp_ns, 2000) * 2000 as \`timestamp_ns\`,argMin(rate_b.value, rate_b.timestamp_ns) as \`value\` from \`rate_b\` group by \`labels\`,\`timestamp_ns\` order by \`labels\` asc,\`timestamp_ns\` asc", - "stream": [], -} -`; - -exports[`should transpile new style 4 1`] = ` -{ - "duration": 1000, - "matrix": true, - "query": "WITH str_sel AS (select DISTINCT \`fingerprint\` from loki.time_series where ((JSONHas(labels, 'test_id') = 1) and (JSONExtractString(labels, 'test_id') = '0.7026038163617259')) and ((JSONHas(labels, 'freq') = 1) and (JSONExtractString(labels, 'freq') = '2'))) select \`labels\`,toUInt64(intDiv(timestamp_ns, 1000000000) * 1000) as \`timestamp_ns\`,toFloat64(0) as \`value\` from loki.samples_vX as \`samples\` left any join (select \`fingerprint\`,\`labels\` from \`loki\`.\`time_series\` as \`time_series\` where ((\`time_series\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (date >= toDate(fromUnixTimestamp(intDiv(1638802620000000000, 1000000000)))) and (date <= toDate(fromUnixTimestamp(intDiv(1638803220000000000, 1000000000)))))) AS time_series on \`samples\`.\`fingerprint\` = time_series.fingerprint where ((\`samples\`.\`timestamp_ns\` between 1638802620000000000 and 1638803220000000000) and (\`samples\`.\`type\` in (0,0))) and (\`samples\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (match(string, '2[0-9]$') = 1) group by \`labels\`,\`timestamp_ns\` order by \`labels\` asc,\`timestamp_ns\` asc", - "stream": [ - [Function], - ], -} -`; - -exports[`should transpile new style 5 1`] = ` -{ - "duration": 1000, - "matrix": false, - "query": "WITH str_sel AS (select DISTINCT \`fingerprint\` from loki.time_series where ((JSONHas(labels, 'test_id') = 1) and (JSONExtractString(labels, 'test_id') = '0.000341166036469831_json'))), sel_a AS (select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\`,JSONExtractKeysAndValues(time_series.labels, 'String') as \`labels\` from loki.samples_vX as \`samples\` left any join (select \`fingerprint\`,\`labels\` from \`loki\`.\`time_series\` as \`time_series\` where ((\`time_series\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (date >= toDate(fromUnixTimestamp(intDiv(1638802620000000000, 1000000000)))) and (date <= toDate(fromUnixTimestamp(intDiv(1638803220000000000, 1000000000)))))) AS time_series on \`samples\`.\`fingerprint\` = time_series.fingerprint where ((\`samples\`.\`timestamp_ns\` between 1638802620000000000 and 1638803220000000000) and (\`samples\`.\`type\` in (0,0))) and (\`samples\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) order by \`timestamp_ns\` desc limit 2000) select * from sel_a order by \`labels\` desc,\`timestamp_ns\` desc", - "stream": [ - [Function], - ], -} -`; - -exports[`should transpile new style 6 1`] = ` -{ - "duration": 1000, - "matrix": false, - "query": "WITH str_sel AS (select DISTINCT \`fingerprint\` from loki.time_series where ((JSONHas(labels, 'test_id') = 1) and (JSONExtractString(labels, 'test_id') = '0.2053747382122484_json'))), sel_a AS (select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\`,JSONExtractKeysAndValues(time_series.labels, 'String') as \`labels\`,arrayFilter((x) -> x.2 != '', [('lbl_repl', if(JSONType(samples.string, 'new_lbl') == 'String', JSONExtractString(samples.string, 'new_lbl'), JSONExtractRaw(samples.string, 'new_lbl')))]) as \`extra_labels\` from loki.samples_vX as \`samples\` left any join (select \`fingerprint\`,\`labels\` from \`loki\`.\`time_series\` as \`time_series\` where ((\`time_series\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (date >= toDate(fromUnixTimestamp(intDiv(1638802620000000000, 1000000000)))) and (date <= toDate(fromUnixTimestamp(intDiv(1638803220000000000, 1000000000)))))) AS time_series on \`samples\`.\`fingerprint\` = time_series.fingerprint where ((\`samples\`.\`timestamp_ns\` between 1638802620000000000 and 1638803220000000000) and (\`samples\`.\`type\` in (0,0))) and (\`samples\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (isValidJSON(samples.string) = 1) and ((indexOf(extra_labels, ('lbl_repl', 'new_val')) > 0) or ((arrayExists(x -> x.1 == 'lbl_repl', extra_labels) = 0) and ((arrayExists(x -> x.1 == 'lbl_repl', labels) = 1) and (arrayFirst(x -> x.1 == 'lbl_repl', labels).2 = 'new_val')))) order by \`timestamp_ns\` desc limit 2000) select * from sel_a order by \`labels\` desc,\`timestamp_ns\` desc", - "stream": [], -} -`; - -exports[`should transpile new style 7 1`] = ` -{ - "duration": 3000, - "matrix": true, - "query": "WITH str_sel AS (select DISTINCT \`fingerprint\` from loki.time_series where ((JSONHas(labels, 'test_id') = 1) and (JSONExtractString(labels, 'test_id') = '0.1547558751138609_json'))) select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,intDiv(samples.timestamp_ns, 1000000) as \`timestamp_ns\`,JSONExtractKeysAndValues(time_series.labels, 'String') as \`labels\` from loki.samples_vX as \`samples\` left any join (select \`fingerprint\`,\`labels\` from \`loki\`.\`time_series\` as \`time_series\` where ((\`time_series\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (date >= toDate(fromUnixTimestamp(intDiv(1638802620000000000, 1000000000)))) and (date <= toDate(fromUnixTimestamp(intDiv(1638803220000000000, 1000000000)))))) AS time_series on \`samples\`.\`fingerprint\` = time_series.fingerprint where ((\`samples\`.\`timestamp_ns\` between 1638802620000000000 and 1638803220000000000) and (\`samples\`.\`type\` in (0,0))) and (\`samples\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) order by \`timestamp_ns\` desc", - "stream": [ - [Function], - [Function], - [Function], - [Function], - [Function], - [Function], - ], -} -`; - -exports[`should transpile new style 8 1`] = ` -{ - "duration": 1000, - "matrix": true, - "query": "WITH str_sel AS (select DISTINCT \`fingerprint\` from loki.time_series where ((JSONHas(labels, 'test_id') = 1) and (JSONExtractString(labels, 'test_id') = '0.4075242197275857'))) select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,intDiv(samples.timestamp_ns, 1000000) as \`timestamp_ns\`,JSONExtractKeysAndValues(time_series.labels, 'String') as \`labels\` from loki.samples_vX as \`samples\` left any join (select \`fingerprint\`,\`labels\` from \`loki\`.\`time_series\` as \`time_series\` where ((\`time_series\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (date >= toDate(fromUnixTimestamp(intDiv(1638802620000000000, 1000000000)))) and (date <= toDate(fromUnixTimestamp(intDiv(1638803220000000000, 1000000000)))))) AS time_series on \`samples\`.\`fingerprint\` = time_series.fingerprint where ((\`samples\`.\`timestamp_ns\` between 1638802620000000000 and 1638803220000000000) and (\`samples\`.\`type\` in (0,0))) and (\`samples\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) order by \`timestamp_ns\` desc", - "stream": [ - [Function], - [Function], - [Function], - [Function], - [Function], - [Function], - ], -} -`; - -exports[`should transpile new style 9 1`] = ` -{ - "duration": 1000, - "matrix": false, - "query": "WITH str_sel AS (select DISTINCT \`fingerprint\` from loki.time_series where ((JSONHas(labels, 'test_id') = 1) and (JSONExtractString(labels, 'test_id') = '0.7186063017626447_json'))), sel_a AS (select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\`,JSONExtractKeysAndValues(time_series.labels, 'String') as \`labels\`,arrayFilter((x) -> x.2 != '', [('sid', if(JSONType(samples.string, 'str_id') == 'String', JSONExtractString(samples.string, 'str_id'), JSONExtractRaw(samples.string, 'str_id')))]) as \`extra_labels\` from loki.samples_vX as \`samples\` left any join (select \`fingerprint\`,\`labels\` from \`loki\`.\`time_series\` as \`time_series\` where ((\`time_series\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (date >= toDate(fromUnixTimestamp(intDiv(1638802620000000000, 1000000000)))) and (date <= toDate(fromUnixTimestamp(intDiv(1638803220000000000, 1000000000)))))) AS time_series on \`samples\`.\`fingerprint\` = time_series.fingerprint where ((\`samples\`.\`timestamp_ns\` between 1638802620000000000 and 1638803220000000000) and (\`samples\`.\`type\` in (0,0))) and (\`samples\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (isValidJSON(samples.string) = 1) and ((arrayExists(x -> x.1 == 'sid' AND (coalesce(toFloat64OrNull(x.2) >= '598', 0)), extra_labels) != 0) or ((arrayExists(x -> x.1 == 'sid', extra_labels) = 0) and (arrayExists(x -> x.1 == 'sid', labels) = 1) and (toFloat64OrNull(arrayFirst(x -> x.1 == 'sid', labels).2) >= '598'))) order by \`timestamp_ns\` desc limit 2000) select * from sel_a order by \`labels\` desc,\`timestamp_ns\` desc", - "stream": [], -} -`; - -exports[`should transpile new style 10 1`] = ` -{ - "duration": 1000, - "matrix": false, - "query": "WITH str_sel AS (select DISTINCT \`fingerprint\` from loki.time_series where ((JSONHas(labels, 'test_id') = 1) and (JSONExtractString(labels, 'test_id') = '0.5505504081219323'))), sel_a AS (select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\`,JSONExtractKeysAndValues(time_series.labels, 'String') as \`labels\`,arrayFilter(x -> x.1 != '' AND x.2 != '', arrayZip(['e'], arrayMap(x -> x[length(x)], extractAllGroupsHorizontal(string, '^([^0-9]+)[0-9]+$')))) as \`extra_labels\` from loki.samples_vX as \`samples\` left any join (select \`fingerprint\`,\`labels\` from \`loki\`.\`time_series\` as \`time_series\` where ((\`time_series\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (date >= toDate(fromUnixTimestamp(intDiv(1638802620000000000, 1000000000)))) and (date <= toDate(fromUnixTimestamp(intDiv(1638803220000000000, 1000000000)))))) AS time_series on \`samples\`.\`fingerprint\` = time_series.fingerprint where ((\`samples\`.\`timestamp_ns\` between 1638802620000000000 and 1638803220000000000) and (\`samples\`.\`type\` in (0,0))) and (\`samples\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) order by \`timestamp_ns\` desc limit 2000) select * from sel_a order by \`labels\` desc,\`timestamp_ns\` desc", - "stream": [], -} -`; - -exports[`should transpile new style 11 1`] = ` -{ - "duration": 1000, - "matrix": true, - "query": "WITH str_sel AS (select DISTINCT \`fingerprint\` from loki.time_series where ((JSONHas(labels, 'label') = 1) and (JSONExtractString(labels, 'label') = 'val'))), uw_rate_a AS (select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,intDiv(samples.timestamp_ns, 1000000) as \`timestamp_ns\`,JSONExtractKeysAndValues(time_series.labels, 'String') as \`labels\`,toFloat64OrNull(arrayFirst(x -> x.1 == 'b', labels).2) as \`unwrapped\` from loki.samples_vX as \`samples\` left any join (select \`fingerprint\`,\`labels\` from \`loki\`.\`time_series\` as \`time_series\` where ((\`time_series\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (date >= toDate(fromUnixTimestamp(intDiv(1638802620000000000, 1000000000)))) and (date <= toDate(fromUnixTimestamp(intDiv(1638803220000000000, 1000000000)))))) AS time_series on \`samples\`.\`fingerprint\` = time_series.fingerprint where ((\`samples\`.\`timestamp_ns\` between 1638802620000000000 and 1638803220000000000) and (\`samples\`.\`type\` in (0,0))) and (\`samples\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and ((arrayExists(x -> x.1 == 'b', labels) = 1) and (isNotNull(unwrapped) = 1)) order by \`timestamp_ns\` desc), uw_rate_b AS (select labels as \`labels\`,SUM(unwrapped) / 1 as \`value\`,intDiv(timestamp_ns, 1000) * 1000 as \`timestamp_ns\` from uw_rate_a group by \`labels\`,\`timestamp_ns\` order by \`labels\`,\`timestamp_ns\`) select \`labels\`,intDiv(timestamp_ns, 2000) * 2000 as \`timestamp_ns\`,argMin(uw_rate_b.value, uw_rate_b.timestamp_ns) as \`value\` from uw_rate_b group by \`labels\`,\`timestamp_ns\` order by \`labels\` asc,\`timestamp_ns\` asc", - "stream": [], -} -`; - -exports[`should transpile new style 12 1`] = ` -{ - "duration": 1000, - "matrix": false, - "query": "WITH str_sel AS (select DISTINCT \`fingerprint\` from loki.time_series where ((JSONHas(labels, 'freq') = 1) and (JSONExtractString(labels, 'freq') = '1'))), sel_a AS (select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\`,JSONExtractKeysAndValues(time_series.labels, 'String') as \`labels\` from loki.samples_vX as \`samples\` left any join (select \`fingerprint\`,\`labels\` from \`loki\`.\`time_series\` as \`time_series\` where ((\`time_series\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) and (date >= toDate(fromUnixTimestamp(intDiv(1638802620000000000, 1000000000)))) and (date <= toDate(fromUnixTimestamp(intDiv(1638803220000000000, 1000000000)))))) AS time_series on \`samples\`.\`fingerprint\` = time_series.fingerprint where ((\`samples\`.\`timestamp_ns\` between 1638802620000000000 and 1638803220000000000) and (\`samples\`.\`type\` in (0,0))) and (\`samples\`.\`fingerprint\` in (select \`fingerprint\` from str_sel)) order by \`timestamp_ns\` desc limit 2000) select * from sel_a order by \`labels\` desc,\`timestamp_ns\` desc", - "stream": [ - [Function], - [Function], - ], -} -`; - -exports[`should transpile plugins 1`] = ` -[ - { - "labels": { - "lbl1": "a", - }, - "timestamp_ns": "0", - "value": 10, - }, - { - "EOF": true, - }, -] -`; - -exports[`should transpile series 1`] = `"WITH idx_sel AS ((select \`sel_1\`.\`fingerprint\` from (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'test_id') and (\`val\` = '123'))) as \`sel_1\`)) select DISTINCT \`fingerprint\`,\`labels\` from loki.time_series where (\`fingerprint\` in (idx_sel)) and (1 == 1)"`; - -exports[`should transpile tail 1`] = ` -{ - "query": "WITH idx_sel AS (select \`sel_1\`.\`fingerprint\` from (select \`fingerprint\` from \`loki\`.\`time_series_gin\` where ((\`key\` = 'test_id') and (match(val, '_ws') = 1))) as \`sel_1\`) select \`samples\`.\`string\` as \`string\`,\`samples\`.\`fingerprint\` as \`fingerprint\`,samples.timestamp_ns as \`timestamp_ns\`,JSONExtractKeysAndValues(time_series.labels, 'String') as \`labels\` from loki.samples_v3 as \`samples\` left any join (select \`fingerprint\`,\`labels\` from \`loki\`.\`time_series\` as \`time_series\` where ((\`time_series\`.\`fingerprint\` in (idx_sel)) and (date >= toDate(fromUnixTimestamp(intDiv((toUnixTimestamp(now()) - 5) * 1000000000, 1000000000)))) and (date <= toDate(fromUnixTimestamp(intDiv(0, 1000000000)))))) AS time_series on \`samples\`.\`fingerprint\` = time_series.fingerprint where ((\`samples\`.\`timestamp_ns\` > (toUnixTimestamp(now()) - 5) * 1000000000) and (\`samples\`.\`type\` in (0,0))) and (samples.fingerprint IN idx_sel) order by \`timestamp_ns\` asc", - "stream": [], -} -`; diff --git a/test/common.js b/test/common.js deleted file mode 100644 index 9582ac40..00000000 --- a/test/common.js +++ /dev/null @@ -1,52 +0,0 @@ -const axios = require('axios') -/** - * - * @param id {string} - * @param frequencySec {number} - * @param startMs {number} - * @param endMs {number} - * @param extraLabels {Object} - * @param msgGen? {(function(number): String)} - * @param valGen? {(function(number): number)} - * @param points {Object} - */ -module.exports.createPoints = (id, frequencySec, - startMs, endMs, - extraLabels, points, msgGen, valGen) => { - const streams = { - test_id: id, - freq: frequencySec.toString(), - ...extraLabels - } - msgGen = msgGen || ((i) => `FREQ_TEST_${i}`) - const values = new Array(Math.floor((endMs - startMs) / frequencySec / 1000)).fill(0) - .map((v, i) => valGen - ? [((startMs + frequencySec * i * 1000) * 1000000).toString(), msgGen(i), valGen(i)] - : [((startMs + frequencySec * i * 1000) * 1000000).toString(), msgGen(i)]) - points = { ...points } - points[JSON.stringify(streams)] = { - stream: streams, - values: values - } - return points -} - -/** - * - * @param points {Object<string, {stream: Object<string, string>, values: [string, string]}>} - * @param endpoint {string} - * @returns {Promise<void>} - */ -module.exports.sendPoints = async (endpoint, points) => { - try { - console.log(`${endpoint}/loki/api/v1/push`) - await axios.post(`${endpoint}/loki/api/v1/push`, { - streams: Object.values(points) - }, { - headers: { 'Content-Type': 'application/json' } - }) - } catch (e) { - console.log(e.response) - throw e - } -} diff --git a/test/e2e b/test/e2e deleted file mode 160000 index 55c595d3..00000000 --- a/test/e2e +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 55c595d3f847e979c8f29bea9b3c9625a0602121 diff --git a/test/insert.benchmark.test.js b/test/insert.benchmark.test.js deleted file mode 100644 index 07a5af07..00000000 --- a/test/insert.benchmark.test.js +++ /dev/null @@ -1,184 +0,0 @@ -const casual = require('casual') -const axios = require('axios') -const logfmt = require('logfmt') - -/** - * This is the Insert benchmark test. - * In order to run the test you have to - * - run clickhouse with appropriate databases - * - provide all the needed environment for cLoki - * - export BENCHMARK=1 and INSERT_BENCHMARK=1 env vars - * - run jest - */ - -const isInsertBenchmarkEnabled = () => process.env.BENCHMARK && process.env.INSERT_BENCHMARK && - parseInt(process.env.BENCHMARK) && parseInt(process.env.INSERT_BENCHMARK) - -const randWords = (min, max) => casual.words(Math.round(Math.random() * (max - min)) + min) - -/** - * - * @param options? {{labels: {number}, fingerprints: {number}}} - * @returns {[string, string][][]} - */ -const genFingerprints = (options) => { - options = options || {} - const labels = new Array(options.labels || 10).fill('').map(() => - randWords(1, 2).replace(/[^a-zA-Z0-9_]/, '_') - ) - return new Array(options.fingerprints || 1000).fill([]).map(() => - labels.map(l => [l, randWords(1, 5)]) - ) -} - -/** - * - * @param labels {string[][]} - * @returns {Object<string, string>} - */ -const labelsToJson = (labels) => labels.reduce((sum, lbl) => { - sum[lbl[0]] = lbl[1] - return sum -}, {}) - -/** - * - * @type {[string, string][][]} - */ -const fingerprints = genFingerprints() - -const genLog = [ - /** - * Random str - * @returns {string} - */ - () => randWords(5, 10), - /** - * Random JSON str - * @returns {string} - */ - () => { - const fp = casual.random_element(fingerprints) - const jsn = [ - casual.random_element(fp), - casual.random_element(fp), - ...((new Array(8)).fill([]).map(() => [randWords(1, 2), randWords(1, 5)])) - ] - return JSON.stringify(labelsToJson(jsn)) - }, - /** - * Random logfmt str - * @returns {string} - */ - () => { - const fp = casual.random_element(fingerprints) - const jsn = [ - casual.random_element(fp), - casual.random_element(fp), - ...((new Array(8)).fill([]).map(() => [randWords(1, 2), randWords(1, 5)])) - ] - return logfmt.stringify(labelsToJson(jsn)) - } - -] - -/** - * - * @param amount {number} - * @param fromMs {number} - * @param toMs {number} - * @returns {Promise<void>} - */ -const sendPoints = async (amount, fromMs, toMs) => { - const points = {} - for (let i = 0; i < amount; i++) { - const fp = casual.random_element(fingerprints) - const strFp = JSON.stringify(fp) - points[strFp] = points[strFp] || { stream: labelsToJson(fp), values: [] } - /* let fromNs = fromMs * 1000000; - let toNs = fromMs * 1000000; */ - points[strFp].values.push([ - casual.integer(fromMs, toMs) * 1000000, // "" + (Math.floor(fromNs + (toNs - fromNs) / amount * i)), - casual.random_element(genLog)() - ]) - } - try { - await axios.post('http://localhost:3100/loki/api/v1/push', { - streams: Object.values(points) - }, { - headers: { 'Content-Type': 'application/json' } - }) - } catch (e) { - console.log(e.response) - throw e - } -} - -/** - * - * @param startMs {number} - * @param endMs {number} - * @param points {number} - */ -const logResults = (startMs, endMs, points) => { - const time = endMs - startMs - console.log(`Sent ${points} logs, ${time}ms (${Math.round(points * 1000 / time)} logs/s)`) -} - -/** - * @param pointsPerReq {number} - * @param reqsPersSec {number} - * @param testLengthMs {number} - * @param fromMs? {number} - * @param toMs? {number} - * @returns {Promise<void>} - */ -/* const insertData = async (pointsPerReq, reqsPersSec, testLengthMs, fromMs, toMs) => { - console.log(`Sending ${pointsPerReq} logs/req, ${reqsPersSec} reqs/sec - ${testLengthMs} msecs...`) - let sendPromises = []; - let sentPoints = 0; - fromMs = fromMs || (new Date()).getTime() - 3600 * 2 * 1000; - toMs = toMs || (new Date()).getTime(); - let start = new Date(); - const i = setInterval(() => { - sendPromises.push(sendPoints(pointsPerReq, fromMs, toMs)); - sentPoints += pointsPerReq; - }, 1000 / reqsPersSec); - await new Promise(f => setTimeout(f, testLengthMs)); - clearInterval(i); - await Promise.all(sendPromises); - let end = new Date(); - logResults(start.getTime(), end.getTime(), sentPoints); -} */ -let l = null -beforeAll(async () => { - if (!isInsertBenchmarkEnabled()) { - return - } - l = require('../cloki') - await new Promise((resolve, reject) => setTimeout(resolve, 500)) -}) -afterAll(() => { - if (!isInsertBenchmarkEnabled()) { - return - } - l.stop() -}) -jest.setTimeout(300000) -it('should insert data', async () => { - if (!isInsertBenchmarkEnabled()) { - return - } - /* await new Promise(f => setTimeout(f, 500)); - for (const i of [1, 10,100]) { - for(const j of [1,10,100]) { - await insertData(i, j, 10000); - } - } */ - console.log('Sending 1 000 000 logs as fast as I can') - const start = new Date() - for (let i = 0; i < 1000; i++) { - await sendPoints(1000, Date.now() - 3600 * 2 * 1000, Date.now()) - } - logResults(start.getTime(), (new Date()).getTime(), 1000000) -}) diff --git a/test/insert.same.data.test.js b/test/insert.same.data.test.js deleted file mode 100644 index abb9cc7c..00000000 --- a/test/insert.same.data.test.js +++ /dev/null @@ -1,45 +0,0 @@ -const fs = require('fs') -const { createPoints, sendPoints } = require('./common') - -/** - * This is the Insert benchmark test. - * In order to run the test you have to - * - run clickhouse with appropriate databases - * - provide all the needed environment for cLoki - * - export LOKI_ENDPOINT=http://....loki endpoint... - * - export SAME_DATA_BENCHMARK=1 env vars - * - run jest - */ - -const sameData = () => process.env.SAME_DATA_BENCHMARK === '1' - -let l = null - -beforeAll(async () => { - if (!sameData()) { - return - } - l = require('../cloki') - await new Promise((resolve) => setTimeout(resolve, 500)) -}) - -afterAll(() => { - sameData() && l.stop() -}) - -it('should stream the same data to loki / cloki', async () => { - if (!sameData()) { - return - } - const testId = Date.now().toString() - console.log(testId) - const start = Date.now() - 60 * 1000 - const end = Date.now() - let points = createPoints(testId, 1, start, end, {}, {}) - points = createPoints(testId, 2, start, end, {}, points) - points = createPoints(testId, 4, start, end, {}, points) - fs.writeFileSync('points.json', JSON.stringify({ streams: Object.values(points) })) - await sendPoints('http://localhost:3100', points) - await sendPoints(process.env.LOKI_ENDPOINT, points) - await new Promise((resolve) => setTimeout(resolve, 1000)) -}) diff --git a/test/jest.setup.js b/test/jest.setup.js deleted file mode 100644 index e98cc2a2..00000000 --- a/test/jest.setup.js +++ /dev/null @@ -1 +0,0 @@ -global.AbortSignal = require('node-abort-controller').AbortSignal diff --git a/test/metric_load_test_init.sh b/test/metric_load_test_init.sh deleted file mode 100755 index c2003015..00000000 --- a/test/metric_load_test_init.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -# Partially prepare of Billy test for cLoki -# HOW TO RUN -# chmod +x metric_load_test_init.sh -# CLICKHOUSE_DB=db_name SENSORS=sensors_number CHECKS=amount_of_ticks_for_sensor MS=time_between_two_ticks_in_ms \ -# ./metric_load_test_init.sh all_flags_for_clickhouse-client_to_connect_to_db - -if [ -z $SENSORS ]; then export SENSORS=1000; fi -if [ -z $CHECKS ]; then export CHECKS=3600; fi -if [ -z $MS ]; then export MS=1000; fi -echo "INSERT TIME SERIES" -echo "INSERT INTO $CLICKHOUSE_DB.time_series (date, fingerprint, labels) SELECT toStartOfDay(now()), number, format('{{\"test_id\":\"LOAD_TEST\", \"id\":\"{0}\"}}', toString(number)) FROM numbers(1000)" -clickhouse-client $@ -q "INSERT INTO $CLICKHOUSE_DB.time_series (date, fingerprint, labels) SELECT toStartOfDay(now()), number, format('{{\"test_id\":\"LOAD_TEST\", \"id\":\"{0}\"}}', toString(number)) FROM numbers($SENSORS)" -t -echo "INSERT TIME SERIES OK" -echo "INSERT SAMPLES" -REQ="INSERT INTO $CLICKHOUSE_DB.samples_v2 (fingerprint, timestamp_ms, value) WITH\ - toUInt64(toUnixTimestamp(NOW())) * 1000 - $CHECKS * $MS as start_time,\ - $SENSORS as num_sensors,\ - $CHECKS * $MS as num_ms,\ - ceil($CHECKS * $MS / 24 * 3600 * 1000) as num_days,\ - 24*60 as num_minutes,\ - 24*60*60*1000 as ms_in_day,\ - 60*1000 as ms_in_min,\ - num_days * num_minutes as total_minutes\ - SELECT\ - number % num_sensors as sensor_id,\ - start_time + (intDiv(intDiv(number, num_sensors) * $MS as mils, ms_in_day) as day) * ms_in_day \ - + (intDiv(mils % ms_in_day, ms_in_min) as minute)*ms_in_min \ - + (mils % ms_in_min) time,\ - 60 + 20*sin(cityHash64(sensor_id)) /* median deviation */\ - + 15*sin(2*pi()/num_days*day) /* seasonal deviation */ \ - + 10*sin(2*pi()/num_minutes*minute)*(1 + rand(1)%100/2000) /* daily deviation */ \ - as temperature\ - FROM numbers_mt($SENSORS * $CHECKS)\ - SETTINGS max_block_size=1048576;" -echo "$REQ" -clickhouse-client $@ -q "$REQ" -t -echo "INSERT SAMPLES OK" diff --git a/test/parser.test.js b/test/parser.test.js deleted file mode 100644 index 24e21967..00000000 --- a/test/parser.test.js +++ /dev/null @@ -1,162 +0,0 @@ -const bnf = require('../parser/bnf') -const regexp = require('../parser/registry/parser_registry/regexp') -const UTILS = require('../lib/utils') - -it('should compile', () => { - let res = bnf.ParseScript('bytes_rate({run="kokoko",u_ru_ru!="lolol",zozo=~"sssss"} |~"atltlt" !~ "rmrmrm" [5m])') - expect(res.rootToken.Children('log_stream_selector_rule').map(c => c.value)).toEqual( - ['run="kokoko"', 'u_ru_ru!="lolol"', 'zozo=~"sssss"'] - ) - expect(res.rootToken.Children('log_pipeline').map(c => c.value)).toEqual( - ['|~"atltlt"', '!~ "rmrmrm"'] - ) - res = bnf.ParseScript( - 'bytes_rate({run="kokoko",u_ru_ru!="lolol",zozo=~"sssss"} |~"atltlt" !~ "rmrmrm" | line_format "{{run}} {{intval }}" [5m])' - ) - expect(res).toBeTruthy() - const tid = 0.1113693742057289 - res = bnf.ParseScript(`{test_id="${tid}"}| line_format "{ \\"str\\":\\"{{_entry}}\\", \\"freq2\\": {{divide freq 2}} }"`) - expect(res).toBeTruthy() -}) - -it('should compile log_stream_selector with ciryllic', () => { - const scr = '{et_dolorem=`тететёąĄ`}' - const script = bnf.ParseScript(scr) - expect(script).toBeTruthy() -}) - -it('should compile strings with escaped quotes', () => { - const res = bnf.ParseScript('bytes_rate({run="kok\\"oko",u_ru_ru!="lolol",zozo=~"sssss"} |~"atltlt" !~ "rmrmrm" [5m])') - expect(res.rootToken.Children('log_stream_selector_rule').map(c => c.value)).toEqual( - ['run="kok\\"oko"', 'u_ru_ru!="lolol"', 'zozo=~"sssss"'] - ) - const res2 = bnf.ParseScript('bytes_rate({run=`kok\\`oko`,u_ru_ru!="lolol",zozo=~"sssss"} |~"atltlt" !~ "rmrmrm" [5m])') - expect(res2.rootToken.Children('log_stream_selector_rule').map(c => c.value)).toEqual( - ['run=`kok\\`oko`', 'u_ru_ru!="lolol"', 'zozo=~"sssss"'] - ) - const res3 = bnf.ParseScript('bytes_rate({run=`kok\\\\\\`oko`,u_ru_ru!="lolol",zozo=~"sssss"} |~"atltlt" !~ "rmrmrm" [5m])') - expect(res3.rootToken.Children('log_stream_selector_rule').map(c => c.value)).toEqual( - ['run=`kok\\\\\\`oko`', 'u_ru_ru!="lolol"', 'zozo=~"sssss"'] - ) -}) - -it('should parse lbl cmp', () => { - const ops = ['>', '<', '>=', '<=', '==', '!='] - for (const op of ops) { - const res = bnf.ParseScript(`{test_id="123345456"} | freq ${op} 4.0`) - expect(res.rootToken.Child('number_label_filter_expression').value).toEqual(`freq ${op} 4.0`) - } - for (const op of ops) { - const res = bnf.ParseScript(`{test_id="123345456"} | freq ${op} 4`) - expect(res.rootToken.Child('number_label_filter_expression').value).toEqual(`freq ${op} 4`) - } -}) - -it('should parse macros', () => { - const res = bnf.ParseScript('test_macro("macro is ok")') - expect(res.rootToken.value).toMatch('test_macro("macro is ok")') - expect(res.rootToken.Child('quoted_str').value).toMatch('"macro is ok"') -}) - -const printTree = (token, indent, buf) => { - buf = buf || '' - if (token.name.match(/^(SCRIPT|SYNTAX|[a-z_]+)$/)) { - buf += new Array(indent).fill(' ').join('') + token.name + ': ' + token.value + '\n' - } - buf = token.tokens.reduce((sum, t) => printTree(t, indent + 1, sum), buf) - return buf -} - -it('should compile regex', () => { - expect(printTree(regexp.internal.compile('abcd\\('), 0)).toMatchSnapshot() - expect(printTree(regexp.internal.compile('(a\\(bc)'), 0)).toMatchSnapshot() - expect(printTree(regexp.internal.compile('(?<label1>a[^\\[\\(\\)]bc)'), 0)).toMatchSnapshot() - expect(printTree(regexp.internal.compile('(a(?<label1>[^\\[\\(\\)]bc))'), 0)).toMatchSnapshot() - expect(printTree(regexp.internal.compile('(a[\\(\\)]+(?<l2>b)(?<label1>[^\\[\\(\\)]bc))'), 0)).toMatchSnapshot() -}) - -it('should process regex', () => { - expect(regexp.internal.extractRegexp('"(?<helper>[a-zA-Z0-9]+)..\\r\\n.(?<token>[a-zA-Z]+)"')) - .toMatchSnapshot() -}) - -it('should get named groups', () => { - const nGroups = (str) => { - const t = regexp.internal.compile(str) - const g = regexp.internal.walk(t, []) - // console.log({n:str, g:g}); - expect(g).toMatchSnapshot() - } - nGroups('abcd\\(') - nGroups('(a\\(bc)') - nGroups('(?<label1>a[^\\[\\(\\)]bc)') - nGroups('(a(?<label1>[^\\[\\(\\)]bc))') - nGroups('(a[\\(\\)]+(?<l2>b)(?<label1>[^\\[\\(\\)]bc))') -}) - -it('should erase names', () => { - const nGroups = (str) => { - const t = regexp.internal.compile(str) - const g = regexp.internal.rmNames(t) - // console.log({n:str, g:g.value}); - expect(g.value).toMatchSnapshot() - } - nGroups('abcd\\(') - nGroups('(a\\(bc)') - nGroups('(?<label1>a[^\\[\\(\\)]bc)') - nGroups('(a(?<label1>[^\\[\\(\\)]bc))') - nGroups('(a[\\(\\)]+(?<l2>b)(?<label1>[^\\[\\(\\)]bc))') -}) - -it('should match 3339', () => { - const { parseStringifiedNanosOrRFC3339 } = require('../lib/utils') - console.log(parseStringifiedNanosOrRFC3339('1985-04-12T23:20:50.52Z')) - console.log(parseStringifiedNanosOrRFC3339('1990-12-31T23:59:60Z')) - console.log(parseStringifiedNanosOrRFC3339('1990-12-31T15:59:60-08:00')) - console.log(parseStringifiedNanosOrRFC3339('1937-01-01T12:00:27.87+00:20')) -}) - -it('should stable stringify', () => { - const stringify = require('json-stable-stringify') - expect(stringify({ a: 'a', b: 'b' })).toEqual(stringify({ b: 'b', a: 'a' })) -}) - -it('should parse duration', () => { - expect(UTILS.parseDurationSecOrDefault('120ms', 5)).toEqual(0.12) - expect(UTILS.parseDurationSecOrDefault('1', 5)).toEqual(1) - expect(UTILS.parseDurationSecOrDefault('145d', 5)).toEqual(5) - expect(UTILS.parseDurationSecOrDefault('trash', 5)).toEqual(5) -}) - -it('should parse invalid expressions fast', () => { - const start = Date.now() - expect(bnf.ParseScript('sum(count_over_time({namespace=~"qefqef", pod=~"3e3e3e3", stream="stdout", container="nginx"} |= `wee` | json request_uri="message.request_uri" | unwrap [5m]))')) - .toBeFalsy() - expect(Date.now() - start).toBeLessThan(1000) -}) - -it('should parse parameterized fns', () => { - let exp = bnf.ParseScript('topk(5, rate({a="b"}[1s]))') - expect(exp.rootToken.value).toEqual('topk(5, rate({a="b"}[1s]))') - - exp = bnf.ParseScript('topk(5, sum(rate({a="b"}[1s])) by (a))') - expect(exp.rootToken.value).toEqual('topk(5, sum(rate({a="b"}[1s])) by (a))') - - exp = bnf.ParseScript('topk(5, rate({a="b"}|unwrap b[1s]) by (a))') - expect(exp.rootToken.value).toEqual('topk(5, rate({a="b"}|unwrap b[1s]) by (a))') - - exp = bnf.ParseScript('topk(5, sum(rate({a="b"}|unwrap b[1s]) by (a)) by (b) > 1)') - expect(exp.rootToken.value).toEqual('topk(5, sum(rate({a="b"}|unwrap b[1s]) by (a)) by (b) > 1)') -}) - -it('should parse a default request', () => { - const req = '{job="svc", level=~"ERR|WRN|INF"} |= `` | json Environment="req[\\"depl.env\\"]", AccountId="att[\\"AccId\\"]", ClientId="att[\\"ClId\\"]", RId="att[\\"Id\\"]", body="body", RequestPath="att[\\"ReqPath\\"]", id="att[\\"id\\"]", Transaction="att[\\"Txn\\"]", Scope="IS[\\"name\\"]", Whence="att[\\"Wnc\\"]", att="att[\\"ex.msg\\"]" | Environment = `A`' - const exp = bnf.ParseScript(req) - expect(exp.rootToken.value).toEqual(req) -}) - -it('should parse a long string', () => { - const req = '{job="svc", level=~"json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24|json1|json2|json3|json4|json5|json6|json7|json8|json9|json10|json11|json12|json13|json14|json15|json16|json17|json18|json19|json20|json21|json22|json23|json24"}' - const exp = bnf.ParseScript(req) - expect(exp.rootToken.value).toEqual(req) -}) diff --git a/test/plugins.test.js b/test/plugins.test.js deleted file mode 100644 index c703d780..00000000 --- a/test/plugins.test.js +++ /dev/null @@ -1,9 +0,0 @@ - -const { getPlg } = require('../plugins/engine') -it('should glob', () => { - expect(getPlg({ type: 'unwrap_registry' })).toBeTruthy() -}) - -it('should unicode chars', () => { - console.log('АąŚĄ'.match(/\p{L}/ug)) -}) diff --git a/test/promwrite.sh b/test/promwrite.sh deleted file mode 100644 index e397b5a3..00000000 --- a/test/promwrite.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -# Execute inside promremotecli folder -# see https://github.com/m3dbx/prometheus_remote_client_golang -rand=`awk -v min=1 -v max=10 'BEGIN{srand(); print int(min+rand()*(max-min+1))}'` -dd=`date +%s` -go run main.go -u http://localhost:3100/api/v1/prom/remote/write -t http:metrics -d $dd,$rand diff --git a/test/qryn_test_env/docker-compose.yaml b/test/qryn_test_env/docker-compose.yaml deleted file mode 100644 index 976db049..00000000 --- a/test/qryn_test_env/docker-compose.yaml +++ /dev/null @@ -1,121 +0,0 @@ -version: '2.1' - -networks: - qryn-test: - driver: bridge - -services: - grafana: - image: grafana/grafana:11.2.2 - container_name: grafana - user: root - networks: - - qryn-test - volumes: - - ./grafana/_data:/var/lib/grafana:rw - - ./grafana/provisioning/:/etc/grafana/provisioning/ - environment: - - GF_SECURITY_ADMIN_USER=${ADMIN_USER:-admin} - - GF_SECURITY_ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin} - - GF_USERS_ALLOW_SIGN_UP=false - - GF_USERS_DEFAULT_THEME=light - - GF_EXPLORE_ENABLED=true - - GF_ALERTING_ENABLED=false - - GF_UNIFIED_ALERTING_ENABLED=true - - GF_FEATURE_TOGGLES_ENABLE=traceToMetrics,publicDashboards,tempoApmTable - - GF_INSTALL_PLUGINS=grafana-pyroscope-app,https://storage.googleapis.com/integration-artifacts/grafana-lokiexplore-app/grafana-lokiexplore-app-latest.zip;grafana-lokiexplore-app - restart: unless-stopped - ports: - - 3000:3000 - depends_on: - - qryn - - clickhouse-server: - image: clickhouse/clickhouse-server:24.1 - container_name: clickhouse-server - hostname: clickhouse - restart: unless-stopped - volumes: - - ./clickhouse/_data:/var/lib/clickhouse - networks: - - qryn-test - environment: - - CLICKHOUSE_USER=qryn - - CLICKHOUSE_PASSWORD=demo - ports: - - 8123:8123 - - 9000:9000 - healthcheck: - test: ['CMD', 'wget', '--spider', '-q', '127.0.0.1:8123/ping'] - interval: 1s - timeout: 1s - retries: 30 - - qryn: - image: node:22 - container_name: qryn - hostname: qryn - restart: unless-stopped - volumes: - - ../../:/app - networks: - - qryn-test - expose: - - 3100 - ports: - - "3100:3100" - environment: - - CLICKHOUSE_SERVER=clickhouse-server - - CLICKHOUSE_PORT=8123 - - CLICKHOUSE_AUTH=qryn:demo - - CLICKHOUSE_DB=qryn - - NODE_OPTIONS="--max-old-space-size=4096" - - FASTIFY_METRICS=true - working_dir: /app - entrypoint: sh - command: - - -c - - "mkdir /_app && cp -rf patches package.json package-lock.json /_app && cd /_app && npm install && cd /app && NODE_PATH='.:../_app/node_modules' node qryn_node.js" - depends_on: - clickhouse-server: - condition: service_healthy - - longtest: - image: golang:1.22-alpine - volumes: - - ./longtest:/longtest - working_dir: /longtest - environment: - MODE: LMZ - URL: http://qryn:3100 - command: ["go", "run", "."] - networks: - - qryn-test - depends_on: - - qryn - - otel-collector: - container_name: otel-collector - hostname: otel-collector - image: ghcr.io/metrico/qryn-otel-collector:0.0.5 - networks: - - qryn-test - volumes: - - ./otel/otel-collector-config.yaml:/etc/otel/config.yaml - ports: - - "3200:3100" # Loki/Logql HTTP receiver - - "3201:3200" # Loki/Logql gRPC receiver - - "8088:8088" # Splunk HEC receiver - - "5514:5514" # Syslog TCP Rereceiverceiver - - "24224:24224" # Fluent Forward receiver - - "4317:4317" # OTLP gRPC receiver - - "4318:4318" # OTLP HTTP receiver - - "14250:14250" # Jaeger gRPC receiver - - "14268:14268" # Jaeger thrift HTTP receiver - - "9411:9411" # Zipkin Trace receiver - - "11800:11800" # Skywalking gRPC receiver - - "12800:12800" # Skywalking HTTP receiver - - "8086:8086" # InfluxDB Line proto HTTP - - "8062:8062" # Pyroscope jprof - restart: on-failure - diff --git a/test/qryn_test_env/grafana/provisioning/dashboards/11159_rev1_custom.json b/test/qryn_test_env/grafana/provisioning/dashboards/11159_rev1_custom.json deleted file mode 100644 index 27cbd5bd..00000000 --- a/test/qryn_test_env/grafana/provisioning/dashboards/11159_rev1_custom.json +++ /dev/null @@ -1,1047 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "description": "node.js prometheus client basic metrics", - "editable": true, - "fiscalYearStartMonth": 0, - "gnetId": 11159, - "graphTooltip": 0, - "id": 6, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 10, - "x": 0, - "y": 0 - }, - "id": 6, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "expr": "irate(process_cpu_user_seconds_total{instance=~\"$instance\"}[2m]) * 100", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "User CPU - {{instance}}", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "expr": "irate(process_cpu_system_seconds_total{instance=~\"$instance\"}[2m]) * 100", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Sys CPU - {{instance}}", - "refId": "B" - } - ], - "title": "Process CPU Usage", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 9, - "x": 10, - "y": 0 - }, - "id": 8, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "expr": "nodejs_eventloop_lag_seconds{instance=~\"$instance\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "title": "Event Loop Lag", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 5, - "x": 19, - "y": 0 - }, - "id": 2, - "interval": "", - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "/^__name__$/", - "values": false - }, - "textMode": "name", - "wideLayout": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "expr": "sum(nodejs_version_info{instance=~\"$instance\"}) by (version)", - "format": "time_series", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{version}}", - "refId": "A" - } - ], - "title": "Node.js Version", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "#F2495C", - "mode": "fixed" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 5, - "x": 19, - "y": 3 - }, - "id": 4, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "expr": "sum(changes(process_start_time_seconds{instance=~\"$instance\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "title": "Process Restart Times", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 16, - "x": 0, - "y": 7 - }, - "id": 7, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "expr": "process_resident_memory_bytes{instance=~\"$instance\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Process Memory - {{instance}}", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "expr": "nodejs_heap_size_total_bytes{instance=~\"$instance\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Heap Total - {{instance}}", - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "expr": "nodejs_heap_size_used_bytes{instance=~\"$instance\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Heap Used - {{instance}}", - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "expr": "nodejs_external_memory_bytes{instance=~\"$instance\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "External Memory - {{instance}}", - "refId": "D" - } - ], - "title": "Process Memory Usage", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 8, - "x": 16, - "y": 7 - }, - "id": 9, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "expr": "nodejs_active_handles_total{instance=~\"$instance\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Active Handler - {{instance}}", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "expr": "nodejs_active_requests_total{instance=~\"$instance\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Active Request - {{instance}}", - "refId": "B" - } - ], - "title": "Active Handlers/Requests Total", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 0, - "y": 14 - }, - "id": 10, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "expr": "nodejs_heap_space_size_total_bytes{instance=~\"$instance\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Heap Total - {{instance}} - {{space}}", - "refId": "A" - } - ], - "title": "Heap Total Detail", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 8, - "y": 14 - }, - "id": 11, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "expr": "nodejs_heap_space_size_used_bytes{instance=~\"$instance\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Heap Used - {{instance}} - {{space}}", - "refId": "A" - } - ], - "title": "Heap Used Detail", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 16, - "y": 14 - }, - "id": 12, - "links": [], - "options": { - "legend": { - "calcs": [ - "mean", - "lastNotNull", - "max", - "min" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "pluginVersion": "10.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "expr": "nodejs_heap_space_size_available_bytes{instance=~\"$instance\"}", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "Heap Used - {{instance}} - {{space}}", - "refId": "A" - } - ], - "title": "Heap Available Detail", - "type": "timeseries" - } - ], - "refresh": "", - "schemaVersion": 38, - "tags": [ - "nodejs" - ], - "templating": { - "list": [ - { - "current": { - "selected": false, - "text": "Prometheus", - "value": "prometheus" - }, - "hide": 0, - "includeAll": false, - "label": "Datasource", - "multi": false, - "name": "DS_PROMETHEUS", - "options": [], - "query": "prometheus", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "type": "datasource" - }, - { - "current": { - "selected": false, - "text": "All", - "value": "$__all" - }, - "datasource": { - "type": "prometheus", - "uid": "prometheus" - }, - "definition": "label_values(nodejs_version_info, instance)", - "hide": 0, - "includeAll": true, - "label": "instance", - "multi": true, - "name": "instance", - "options": [], - "query": "label_values(nodejs_version_info, instance)", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 1, - "tagValuesQuery": "", - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-1h", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "timezone": "", - "title": "NodeJS Application Dashboard", - "uid": "PTSqcpJWk1", - "version": 3, - "weekStart": "" -} \ No newline at end of file diff --git a/test/qryn_test_env/grafana/provisioning/dashboards/default.yaml b/test/qryn_test_env/grafana/provisioning/dashboards/default.yaml deleted file mode 100644 index 7c4d0dd5..00000000 --- a/test/qryn_test_env/grafana/provisioning/dashboards/default.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: 1 - -providers: - - name: Qryn NodeJS Application Dashboard - folder: Services - type: file - options: - path: /etc/grafana/provisioning/dashboards/11159_rev1_custom.json \ No newline at end of file diff --git a/test/qryn_test_env/grafana/provisioning/datasources/datasource.yml b/test/qryn_test_env/grafana/provisioning/datasources/datasource.yml deleted file mode 100644 index 8902c532..00000000 --- a/test/qryn_test_env/grafana/provisioning/datasources/datasource.yml +++ /dev/null @@ -1,80 +0,0 @@ -# config file version -apiVersion: 1 - -deleteDatasources: - - name: Loki - orgId: 1 - - name: Tempo - orgId: 1 - - name: Prometheus - orgId: 1 - - name: Pyroscope - orgId: 1 - -datasources: - - name: Loki - type: loki - access: proxy - uid: loki - url: http://qryn:3100 - editable: true - jsonData: - derivedFields: - - datasourceUid: tempo - matcherRegex: "^.*?traceI[d|D]=(\\w+).*$" - name: traceId - url: '$${__value.raw}' - - datasourceUid: tempo - matcherRegex: "^.*?\"traceID\":\"(\\w+)\".*$" - name: traceID - url: '$${__value.raw}' - - name: Tempo - type: tempo - access: proxy - uid: tempo - url: http://qryn:3100 - editable: true - jsonData: - nodeGraph: - enabled: true - tracesToLogs: - datasourceUid: loki - filterByTraceID: false - spanEndTimeShift: "2000ms" - spanStartTimeShift: "-2000ms" - tags: ['job'] - tracesToMetrics: - datasourceUid: prometheus - tags: ['job'] - serviceMap: - datasourceUid: prometheus - lokiSearch: - datasourceUid: loki - - name: Prometheus - type: prometheus - access: proxy - uid: prometheus - url: http://qryn:3100 - editable: true - basicAuth: false - jsonData: - httpMethod: GET - graphiteVersion: "1.1" - tlsAuth: false - tlsAuthWithCACert: false - cacheLevel: 'High' - incrementalQuerying: 'Enable' - incrementalQueryOverlapWindow: 2m - - name: Pyroscope - uid: pyroscope - type: grafana-pyroscope-datasource - version: 2 - access: proxy - url: http://qryn:3100 - basicAuth: false - basicAuthUser: "" - withCredentials: false - isDefault: false - readOnly: false - # secureJsonData: - # apiKey: {YOUR_API_KEY} diff --git a/test/qryn_test_env/longtest/README.md b/test/qryn_test_env/longtest/README.md deleted file mode 100644 index 2e68e484..00000000 --- a/test/qryn_test_env/longtest/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# gigaswap - long test - -long-term test of qryn endpoint -It sends -- 3000 logs/sec -- 3000 influx logs / sec -- 300 zipkin traces/sec -- 300 datadog traces/sec with variable amount of spans -- 5K of metrics / 15 sec - -depending on the MODE until you stop it. - -# Usage - -- go build -o longtest -- URL='<base url like http://localhost:1234>' MODE=<MODE LIST LMZDIC> ZPATH=/path/for/zipkin DPATH=/path/for/datadog ./longtest - -## MODE LIST - -- L - for loki logs -- M - for metrics remote-write -- Z - for zipkin traces -- D - for datadog traces -- I - for influx logs -- C - for a lot of small simultaneous loki log request to check batching -- S - for servicegraph testing diff --git a/test/qryn_test_env/longtest/common.go b/test/qryn_test_env/longtest/common.go deleted file mode 100644 index 3a9ec37d..00000000 --- a/test/qryn_test_env/longtest/common.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -import "math/rand" - -func pickRandom[T any](arr []T, rnd *rand.Rand) T { - return arr[rnd.Intn(len(arr))] -} diff --git a/test/qryn_test_env/longtest/datadogSender.go b/test/qryn_test_env/longtest/datadogSender.go deleted file mode 100644 index 5a048cb5..00000000 --- a/test/qryn_test_env/longtest/datadogSender.go +++ /dev/null @@ -1,132 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "math/rand" - "os" - "time" -) - -// https://docs.datadoghq.com/tracing/guide/send_traces_to_agent_by_api/#model -type DataDogModel struct { - Duration int64 `json:"duration"` - Error int32 `json:"error"` - Meta map[string]string `json:"meta"` - Metrics map[string]float64 `json:"metrics"` - Name string `json:"name"` - ParentID int64 `json:"parent_id"` - Resource string `json:"resource"` - Service string `json:"service"` - SpanID int64 `json:"span_id"` - Start int64 `json:"start"` - TraceID int64 `json:"trace_id"` - Type DataDogModelEnum `json:"type"` -} - -type DataDogModelEnum string - -func (d DataDogModelEnum) String() string { - return string(d) -} - -const ( - DataDogModelEnumWeb DataDogModelEnum = "web" - DataDogModelEnumDb DataDogModelEnum = "db" - DataDogModelEnumCache DataDogModelEnum = "cache" - DataDogModelEnumCustom DataDogModelEnum = "custom" -) - -type DatadogReq [][]DataDogModel - -func (d DatadogReq) Serialize() ([]byte, error) { - return json.Marshal(d) -} - -func NewDatadogSender(opts LogSenderOpts) ISender { - rnd := rand.New(rand.NewSource(time.Now().UnixNano())) - path := "/v0.3/traces" - if os.Getenv("DPATH") != "" { - path = os.Getenv("DPATH") - } - l := &GenericSender{ - LogSenderOpts: opts, - rnd: rnd, - timeout: time.Second, - path: path, - } - pickCont := func() string { - return l.pickRandom(opts.Containers) - } - l.generate = func() IRequest { - - var ( - spansPerTrace int = 3 - traces int = 0 - remainder int = 0 - ) - - // if the total is less than the spans per trace, we only do those. - if opts.LinesPS < spansPerTrace { - remainder = opts.LinesPS - } else { - traces = opts.LinesPS / spansPerTrace - remainder = opts.LinesPS % spansPerTrace - } - - // make sure we always have an array with the correct amount of slots. - arrayLength := traces - if remainder != 0 { - arrayLength++ - } - - // initiate the main container - req := make(DatadogReq, arrayLength) - // add the traces that fit - for i := 0; i < traces; i++ { - req[i] = trace(i, spansPerTrace, rnd, pickCont) - } - - // add a last trace with the remaining spans. - if remainder != 0 { - req[traces] = trace(traces, remainder, rnd, pickCont) - } - - return req - } - return l -} - -func trace(i int, spans int, rnd *rand.Rand, pickCont func() string) []DataDogModel { - var ( - traceID = rnd.Int63n(10000000) - tr = make([]DataDogModel, spans) - ) - - for j := 0; j < spans; j++ { - cont := pickCont() - now := time.Now() - - tr[j] = DataDogModel{ - Duration: time.Duration(1 * (i + 1)).Nanoseconds(), - Error: 0, - Meta: map[string]string{ - "sender": "longtest", - "randomContainer": cont, - }, - Metrics: map[string]float64{ - REQ_BYTES: rnd.Float64(), - }, - Name: fmt.Sprintf("longtest-%d-%d", i+1, j+1), - ParentID: 0, - Resource: "/", - Service: "longtest", - SpanID: int64((i + 1) * (j + 1)), - Start: now.UnixNano(), - TraceID: traceID, - Type: DataDogModelEnumWeb, - } - } - - return tr -} diff --git a/test/qryn_test_env/longtest/generate.go b/test/qryn_test_env/longtest/generate.go deleted file mode 100644 index c4698e51..00000000 --- a/test/qryn_test_env/longtest/generate.go +++ /dev/null @@ -1,87 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "github.com/akvlad/flog/generator" - "github.com/scaleway/scaleway-sdk-go/namegenerator" - "strconv" - "sync" - "time" -) - -type bufCloser struct { - *bytes.Buffer -} - -func (*bufCloser) Close() error { - return nil -} - -func generateLogs() []string { - var res []string - writers := make([]bytes.Buffer, 8) - wg := sync.WaitGroup{} - for i, format := range []string{"apache_common", "apache_combined", "apache_error", "rfc3164", "rfc5424", - "common_log", "json"} { - wg.Add(1) - go func(format string, i int) { - defer wg.Done() - generator.Generate(&generator.Option{ - Format: format, - Output: "", - Type: "stdout", - Number: 0, - Bytes: 10 * 1024 * 1024, - Sleep: 0, - Delay: 0, - SplitBy: 0, - Overwrite: false, - Forever: false, - Writer: &bufCloser{&writers[i]}, - }) - }(format, i) - } - generateFaro(&writers[7]) - wg.Wait() - for _, w := range writers { - lines := bytes.Split(w.Bytes(), []byte("\n")) - for _, l := range lines { - res = append(res, string(l)) - } - } - return res -} - -func generateFaro(buf *bytes.Buffer) { - _buf := bytes.Buffer{} - - generator.Generate(&generator.Option{ - Format: "common_log", - Output: "", - Type: "stdout", - Number: 0, - Bytes: 10 * 1024 * 1024, - Sleep: 0, - Delay: 0, - SplitBy: 0, - Overwrite: false, - Forever: false, - Writer: &bufCloser{&_buf}, - }) - - lines := bytes.Split(_buf.Bytes(), []byte("\n")) - for _, l := range lines { - buf.WriteString(fmt.Sprintf( - "timestamp=\"%s\" kind=log message=%s level=log sdk_name=@grafana/faro-core sdk_version=1.0.0 sdk_integrations=@grafana/faro-web-sdk:instrumentation-errors:1.0.0,@grafana/faro-web-sdk:instrumentation-web-vitals:1.0.0,@grafana/faro-web-sdk:instrumentation-session:1.0.32,@grafana/faro-web-sdk:instrumentation-view:1.0.32,@grafana/faro-web-sdk:instrumentation-console:1.0.0,@grafana/faro-web-tracing:1.0.0,@grafana/faro-react:1.0.0 app_name=@grafana/faro-demo-client app_version=1.0.0 app_environment=production session_id=fDKz3Gccz6 page_url=http://localhost:5173/ browser_name=Firefox browser_version=122.0 browser_os=\"Ubuntu unknown\" browser_mobile=false view_name=default\n", - time.Now().UTC().Format(time.RFC3339Nano), strconv.Quote(string(l)))) - } -} - -func generateNames(n int) []string { - names := make([]string, n) - for i := range names { - names[i] = namegenerator.GetRandomName() - } - return names -} diff --git a/test/qryn_test_env/longtest/genericSender.go b/test/qryn_test_env/longtest/genericSender.go deleted file mode 100644 index 3f9d98f8..00000000 --- a/test/qryn_test_env/longtest/genericSender.go +++ /dev/null @@ -1,191 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "math/rand" - "net/http" - "sync" - "time" -) - -const ( - REQ_OK = "req_ok" - REQ_ERR = "req_err" - REQ_FAIL = "req_fail" - REQ_BYTES = "req_bytes" - REQ_TIME_MS = "req_time_ms" -) - -type IRequest interface { - Serialize() ([]byte, error) -} - -type LogStream struct { - Stream map[string]string `json:"stream"` - Values [][]interface{} `json:"values"` -} - -type LogRequest struct { - Streams []*LogStream `json:"streams"` -} - -func (l *LogRequest) Serialize() ([]byte, error) { - return json.Marshal(l) -} - -type ISender interface { - Run() - Stop() -} - -type LogSenderOpts struct { - Containers []string - Lines []string - LinesPS int - URL string - Headers map[string]string - ID string -} - -type GenericSender struct { - LogSenderOpts - mtx sync.Mutex - rnd *rand.Rand - ticker *time.Ticker - timeout time.Duration - path string - generate func() IRequest - numOfSends int -} - -func (l *GenericSender) Run() { - if l.ticker != nil { - return - } - l.ticker = time.NewTicker(l.timeout) - go func() { - for range l.ticker.C { - if l.generate == nil { - fmt.Println("ERROR! No generate function") - } - numOfSends := l.numOfSends - if numOfSends == 0 { - numOfSends = 1 - } - for i := 0; i < numOfSends; i++ { - err := l.send(l.generate()) - if err != nil { - fmt.Printf("%v\n", err) - continue - } - } - } - }() -} - -func (l *GenericSender) Stop() { - if l.ticker != nil { - l.ticker.Stop() - l.ticker = nil - } -} - -func (l *GenericSender) random(n int) int { - l.mtx.Lock() - defer l.mtx.Unlock() - return l.rnd.Intn(n) -} - -func (l *GenericSender) pickRandom(array []string) string { - if len(array) == 0 { - return "" - } - l.mtx.Lock() - defer l.mtx.Unlock() - return pickRandom[string](array, l.rnd) -} - -func (l *GenericSender) send(request IRequest) error { - retries := 0 - body, err := request.Serialize() - if err != nil { - return err - } - send := func(url string, count bool) { - if url == "" { - url = l.URL + l.path - } - var statsInc = func(name string) { - if count { - stats.Inc(name) - } - } - var statsObserve = func(name string, value int64) { - if count { - stats.Observe(name, value) - } - } - for { - start := time.Now() - req, err := http.NewRequest("POST", url, bytes.NewReader(body)) - if err != nil { - fmt.Printf("Request error: %v\n", err) - <-time.After(time.Second) - if retries < 10 { - statsInc(REQ_ERR) - retries++ - continue - } else { - statsInc(REQ_FAIL) - return - } - } - req.Header.Set("Content-Type", "application/json") - for k, v := range l.Headers { - req.Header.Set(k, v) - } - client := http.Client{ - Timeout: 30 * time.Second, - } - resp, err := client.Do(req) - if err != nil { - fmt.Printf("Request error: %v\n", err) - <-time.After(time.Second) - if retries < 10 { - statsInc(REQ_ERR) - retries++ - continue - } else { - statsInc(REQ_FAIL) - return - } - } - if resp.StatusCode/100 != 2 { - b := bytes.Buffer{} - io.Copy(&b, resp.Body) - fmt.Printf("Request error: [%d]: %s\n", resp.StatusCode, string(b.Bytes())) - <-time.After(time.Second) - if retries < 10 { - statsInc(REQ_ERR) - retries++ - continue - } else { - stats.Inc(REQ_FAIL) - return - } - } - statsInc(REQ_OK) - statsObserve(REQ_BYTES, int64(len(body))) - statsObserve(REQ_TIME_MS, time.Now().Sub(start).Milliseconds()) - return - } - } - go func() { - send("", true) - }() - - return nil -} diff --git a/test/qryn_test_env/longtest/go.mod b/test/qryn_test_env/longtest/go.mod deleted file mode 100644 index 906fab90..00000000 --- a/test/qryn_test_env/longtest/go.mod +++ /dev/null @@ -1,50 +0,0 @@ -module longtest - -go 1.18 - -replace k8s.io/client-go v12.0.0+incompatible => k8s.io/client-go v0.22.1 - -require ( - github.com/akvlad/flog v0.4.4-0.20220607095327-09ef70e4099e - github.com/apache/arrow/go/v13 v13.0.0-20230607163259-9be7074f85d6 - github.com/golang/protobuf v1.5.3 - github.com/golang/snappy v0.0.4 - github.com/influxdata/influxdb-client-go/v2 v2.12.2 - github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf - github.com/openzipkin/zipkin-go v0.4.1 - github.com/prometheus/prometheus v0.42.0 - github.com/scaleway/scaleway-sdk-go v1.0.0-beta.13 - go.opentelemetry.io/proto/otlp v0.19.0 - google.golang.org/protobuf v1.33.0 -) - -require ( - github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c // indirect - github.com/andybalholm/brotli v1.0.4 // indirect - github.com/apache/thrift v0.16.0 // indirect - github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect - github.com/brianvoe/gofakeit v3.18.0+incompatible // indirect - github.com/deepmap/oapi-codegen v1.12.4 // indirect - github.com/goccy/go-json v0.10.0 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/flatbuffers v23.1.21+incompatible // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/klauspost/asmfmt v1.3.2 // indirect - github.com/klauspost/compress v1.15.15 // indirect - github.com/klauspost/cpuid/v2 v2.2.3 // indirect - github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect - github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect - github.com/pierrec/lz4/v4 v4.1.17 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/zeebo/xxh3 v1.0.2 // indirect - golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect - golang.org/x/mod v0.8.0 // indirect - golang.org/x/net v0.9.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.7.0 // indirect - golang.org/x/text v0.9.0 // indirect - golang.org/x/tools v0.6.0 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect - google.golang.org/grpc v1.56.3 // indirect -) diff --git a/test/qryn_test_env/longtest/go.sum b/test/qryn_test_env/longtest/go.sum deleted file mode 100644 index e55601b5..00000000 --- a/test/qryn_test_env/longtest/go.sum +++ /dev/null @@ -1,513 +0,0 @@ -bou.ke/monkey v1.0.1 h1:zEMLInw9xvNakzUUPjfS4Ds6jYPqCFx3m7bRmG5NH2U= -bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= -github.com/akvlad/flog v0.4.4-0.20220607095327-09ef70e4099e h1:3AbWEyBxLZuELxe9FVBLrj97iqiST4FqXCNx0Afw8qA= -github.com/akvlad/flog v0.4.4-0.20220607095327-09ef70e4099e/go.mod h1:0mcqdrwr9BJQeJ4pPjTh4ENgp0+lJfybTYMjJGV2wM4= -github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/v13 v13.0.0-20230607163259-9be7074f85d6 h1:vaX/XjM1MG7TMtkkESfs1B1/vIfvxSdFVwg/yM9rCho= -github.com/apache/arrow/go/v13 v13.0.0-20230607163259-9be7074f85d6/go.mod h1:W69eByFNO0ZR30q1/7Sr9d83zcVZmF2MiP3fFYAWJOc= -github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY= -github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= -github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= -github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= -github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= -github.com/brianvoe/gofakeit v3.11.5+incompatible/go.mod h1:kfwdRA90vvNhPutZWfH7WPaDzUjz+CZFqG+rPkOjGOc= -github.com/brianvoe/gofakeit v3.18.0+incompatible h1:wDOmHc9DLG4nRjUVVaxA+CEglKOW72Y5+4WNxUIkjM8= -github.com/brianvoe/gofakeit v3.18.0+incompatible/go.mod h1:kfwdRA90vvNhPutZWfH7WPaDzUjz+CZFqG+rPkOjGOc= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deepmap/oapi-codegen v1.12.4 h1:pPmn6qI9MuOtCz82WY2Xaw46EQjgvxednXXrP7g5Q2s= -github.com/deepmap/oapi-codegen v1.12.4/go.mod h1:3lgHGMu6myQ2vqbbTXH2H1o4eXFTGnFiDaOaKKl5yas= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= -github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/flatbuffers v23.1.21+incompatible h1:bUqzx/MXCDxuS0hRJL2EfjyZL3uQrPbMocUa8zGqsTA= -github.com/google/flatbuffers v23.1.21+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/influxdata/influxdb-client-go/v2 v2.12.2 h1:uYABKdrEKlYm+++qfKdbgaHKBPmoWR5wpbmj6MBB/2g= -github.com/influxdata/influxdb-client-go/v2 v2.12.2/go.mod h1:YteV91FiQxRdccyJ2cHvj2f/5sq4y4Njqu1fQzsQCOU= -github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf h1:7JTmneyiNEwVBOHSjoMxiWAqB992atOeepeFYegn5RU= -github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= -github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= -github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= -github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= -github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= -github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mingrammer/cfmt v1.0.0/go.mod h1:D2ZhJie9PURSfLSb+DB3l+aE32t+K7B596FI6rdm/pI= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= -github.com/openzipkin/zipkin-go v0.4.1 h1:kNd/ST2yLLWhaWrkgchya40TJabe8Hioj9udfPcEO5A= -github.com/openzipkin/zipkin-go v0.4.1/go.mod h1:qY0VqDSN1pOBN94dBc6w2GJlWLiovAyg7Qt6/I9HecM= -github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc= -github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/prometheus v0.42.0 h1:G769v8covTkOiNckXFIwLx01XE04OE6Fr0JPA0oR2nI= -github.com/prometheus/prometheus v0.42.0/go.mod h1:Pfqb/MLnnR2KK+0vchiaH39jXxvLMBk+3lnIGP4N7Vk= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.13 h1:n5J2K6g/kl/iT6mODjCoSoRBGQVmIG3aMtYbofi9kxc= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.13/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= -github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= -github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg= -golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gonum.org/v1/gonum v0.12.0 h1:xKuo6hzt+gMav00meVPUlXwSdoEJP46BR+wdxQEFK2o= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= -google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/test/qryn_test_env/longtest/influxSender.go b/test/qryn_test_env/longtest/influxSender.go deleted file mode 100644 index 3693e9fd..00000000 --- a/test/qryn_test_env/longtest/influxSender.go +++ /dev/null @@ -1,51 +0,0 @@ -package main - -import ( - "bytes" - influx "github.com/influxdata/influxdb-client-go/v2" - "github.com/influxdata/influxdb-client-go/v2/api/write" - lp "github.com/influxdata/line-protocol" - "math/rand" - "time" -) - -type InfluxReq []*write.Point - -func (i InfluxReq) Serialize() ([]byte, error) { - var buffer bytes.Buffer - e := lp.NewEncoder(&buffer) - e.SetFieldTypeSupport(lp.UintSupport) - e.FailOnFieldErr(true) - e.SetPrecision(time.Nanosecond) - for _, item := range i { - _, err := e.Encode(item) - if err != nil { - return nil, err - } - } - return buffer.Bytes(), nil -} - -func NewInfluxSender(opts LogSenderOpts) ISender { - l := &GenericSender{ - LogSenderOpts: opts, - rnd: rand.New(rand.NewSource(time.Now().UnixNano())), - timeout: time.Second, - path: "/influx/api/v2/write", - } - l.generate = func() IRequest { - points := make(InfluxReq, opts.LinesPS) - for i := range points { - points[i] = influx.NewPoint("syslog", map[string]string{ - "container": l.pickRandom(l.Containers), - "level": l.pickRandom([]string{"info", "debug", "error"}), - "sender": "logtest", - "endpoint": "influx", - }, map[string]interface{}{ - "message": l.pickRandom(opts.Lines), - }, time.Now()) - } - return points - } - return l -} diff --git a/test/qryn_test_env/longtest/jsonConsistencyChecker.go b/test/qryn_test_env/longtest/jsonConsistencyChecker.go deleted file mode 100644 index 437f17a3..00000000 --- a/test/qryn_test_env/longtest/jsonConsistencyChecker.go +++ /dev/null @@ -1,45 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "math/rand" - "sync" - "time" -) - -func NewJSONConsistencyChecker(opts LogSenderOpts) ISender { - res := &GenericSender{ - LogSenderOpts: opts, - mtx: sync.Mutex{}, - rnd: rand.New(rand.NewSource(time.Now().UnixNano())), - timeout: time.Second, - path: "/loki/api/v1/push", - numOfSends: 10, - } - res.generate = func() IRequest { - logLen := 0 - req := &LogRequest{} - for logLen < 10 { - streamLen := 2 - stream := &LogStream{ - Stream: map[string]string{ - "container": res.pickRandom(res.Containers), - "level": res.pickRandom([]string{"info", "debug", "error"}), - "sender": "consistency-checker", - }, - Values: make([][]interface{}, streamLen), - } - for i := 0; i < streamLen; i++ { - t := fmt.Sprintf("%d", time.Now().UnixNano()) - line, _ := json.Marshal(stream.Stream) - line = append(line, []byte(", t="+t)...) - stream.Values[i] = []interface{}{t, string(line)} - logLen++ - } - req.Streams = append(req.Streams, stream) - } - return req - } - return res -} diff --git a/test/qryn_test_env/longtest/logReader.go b/test/qryn_test_env/longtest/logReader.go deleted file mode 100644 index 81dda4c6..00000000 --- a/test/qryn_test_env/longtest/logReader.go +++ /dev/null @@ -1,103 +0,0 @@ -package main - -import ( - "fmt" - "math/rand" - "sync" - "time" -) - -const ( - LOG_READ_MODE_RAW = 1 - LOG_READ_MODE_LRA = 2 - LOG_READ_MODE_AGG_OP = 3 - LOG_READ_MODE_UNWRAP = 4 - LOG_READ_MODE_UNWRAP_AGG_OP = 5 -) - -type LogReader struct { - Url string - mtx sync.Mutex - rand *rand.Rand -} - -func NewLogReader(url string) *LogReader { - return &LogReader{ - Url: url, - rand: rand.New(rand.NewSource(time.Now().UnixNano())), - } -} - -func (l *LogReader) ReadLogs(mode int) { - containers := l.getValues("container") - switch mode { - case LOG_READ_MODE_RAW: - l.rawRequest(containers) - break - case LOG_READ_MODE_LRA: - l.logRangeAggregationRequest(containers) - break - case LOG_READ_MODE_AGG_OP: - l.aggregationOperatorRequest(containers) - break - case LOG_READ_MODE_UNWRAP: - l.unwrapRequest(containers) - break - case LOG_READ_MODE_UNWRAP_AGG_OP: - l.unwrapAggregationOperatorRequest(containers) - break - } -} - -func (l *LogReader) rawRequest(containers []string) { - l.mtx.Lock() - cnt := pickRandom(containers, l.rand) - to := time.Now().UnixNano() - l.rand.Int63n(600000000000) - from := to - l.rand.Int63n(3600000000000) - l.mtx.Unlock() - l.request(fmt.Sprintf("{sender=\"logtest\", container=\"%s\"}", cnt), from, to) -} - -func (l *LogReader) logRangeAggregationRequest(containers []string) { - l.mtx.Lock() - cnt := pickRandom(containers, l.rand) - to := time.Now().UnixNano() - l.rand.Int63n(600000000000) - from := to - l.rand.Int63n(3600000000000) - l.mtx.Unlock() - l.request(fmt.Sprintf("rate({sender=\"logtest\", container=\"%s\"}[1m])", cnt), from, to) -} - -func (l *LogReader) aggregationOperatorRequest(containers []string) { - l.mtx.Lock() - cnt := pickRandom(containers, l.rand) - to := time.Now().UnixNano() - l.rand.Int63n(600000000000) - from := to - l.rand.Int63n(3600000000000) - l.mtx.Unlock() - l.request(fmt.Sprintf("sum by (level) (rate({sender=\"logtest\", container=\"%s\"}[1m]))", cnt), from, to) -} - -func (l *LogReader) unwrapRequest(containers []string) { - l.mtx.Lock() - cnt := pickRandom(containers, l.rand) - to := time.Now().UnixNano() - l.rand.Int63n(600000000000) - from := to - l.rand.Int63n(3600000000000) - l.mtx.Unlock() - l.request(fmt.Sprintf("rate({sender=\"logtest\", container=\"%s\"} | unwrap_value [1m])", cnt), from, to) -} - -func (l *LogReader) unwrapAggregationOperatorRequest(containers []string) { - l.mtx.Lock() - cnt := pickRandom(containers, l.rand) - to := time.Now().UnixNano() - l.rand.Int63n(600000000000) - from := to - l.rand.Int63n(3600000000000) - l.mtx.Unlock() - l.request(fmt.Sprintf("sum by (sender) (rate({sender=\"logtest\", container=\"%s\"} | unwrap_value [1m]))", cnt), from, to) -} - -func (l *LogReader) request(req string, from int64, to int64) { - -} - -func (l *LogReader) getValues(name string) []string { - return nil -} diff --git a/test/qryn_test_env/longtest/logSender.go b/test/qryn_test_env/longtest/logSender.go deleted file mode 100644 index 97635fbe..00000000 --- a/test/qryn_test_env/longtest/logSender.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "fmt" - "math/rand" - "sync" - "time" -) - -type LogSender struct { - LogSenderOpts - mtx sync.Mutex - rnd *rand.Rand - ticker *time.Ticker - path string -} - -func NewLogSender(opts LogSenderOpts) ISender { - var l *GenericSender - l = &GenericSender{ - LogSenderOpts: opts, - mtx: sync.Mutex{}, - rnd: rand.New(rand.NewSource(time.Now().UnixNano())), - timeout: time.Second, - path: "/loki/api/v1/push", - generate: func() IRequest { - logLen := 0 - req := &LogRequest{} - for logLen < l.LinesPS { - streamLen := 20 - stream := &LogStream{ - Stream: map[string]string{ - "orgid": opts.Headers["X-Scope-OrgID"], - "container": l.pickRandom(l.Containers), - "level": l.pickRandom([]string{"info", "debug", "error"}), - "sender": "logtest", - "__name__": "logs", - "__ttl_days__": "25", - }, - Values: make([][]interface{}, streamLen), - } - for i := 0; i < streamLen; i++ { - //line := fmt.Sprintf("opaqueid=%d mos=%f", l.random(1000), float64(l.random(1000)/250)) - line := l.pickRandom(l.Lines) - stream.Values[i] = []interface{}{fmt.Sprintf("%d", time.Now().UnixNano()), line} - logLen++ - } - req.Streams = append(req.Streams, stream) - } - return req - }, - } - return l -} diff --git a/test/qryn_test_env/longtest/longtest.go b/test/qryn_test_env/longtest/longtest.go deleted file mode 100644 index 1868c5f4..00000000 --- a/test/qryn_test_env/longtest/longtest.go +++ /dev/null @@ -1,188 +0,0 @@ -package main - -import ( - "fmt" - "os" - "strconv" - "strings" - "time" -) - -func main() { - kind := os.Getenv("KIND") - if kind == "" { - kind = "WRITE" - } - switch kind { - case "WRITE": - writeTest() - break - case "READ": - readTest() - break - } -} - -func writeTest() { - fmt.Println("GENERATING") - logs := generateLogs() - //names := generateNames(1500) - fmt.Println("SENDING") - names := generateNames(3300) - power := 1 - if os.Getenv("POWER") != "" { - var err error - power, err = strconv.Atoi(os.Getenv("POWER")) - if err != nil { - panic(err) - } - } - headers := map[string]string{} - if strings.Contains(os.Getenv("MODE"), "L") { - fmt.Println("Run logs") - sender := NewLogSender(LogSenderOpts{ - ID: "logs", - Containers: names, - Lines: logs, - LinesPS: 120 * power, - URL: os.Getenv("URL"), - Headers: headers, - }) - sender.Run() - } - /*if strings.Contains(os.Getenv("MODE"), "P") { - fmt.Println("Run logs PB") - _headers := make(map[string]string, 20) - for k, v := range headers { - _headers[k] = v - } - _headers["Content-Type"] = "application/x-protobuf" - sender := NewPBSender(LogSenderOpts{ - ID: "logs", - Containers: names, - Lines: logs, - LinesPS: 50000, - URL: os.Getenv("URL"), - Headers: _headers, - }) - sender.Run() - }*/ - if strings.Contains(os.Getenv("MODE"), "M") { - fmt.Println("Run metrics") - metrics := NewMetricSender(LogSenderOpts{ - ID: "metrics", - Containers: names, - Lines: logs, - LinesPS: 30 * power, - URL: os.Getenv("URL"), - Headers: headers, - }) - metrics.Run() - } - if strings.Contains(os.Getenv("MODE"), "Z") { - fmt.Println("Run zipkin") - zipkins := NewZipkinSender(LogSenderOpts{ - ID: "traces", - Containers: names, - Lines: logs, - LinesPS: 40 * power, - URL: os.Getenv("URL"), - Headers: headers, - }) - zipkins.Run() - } - if strings.Contains(os.Getenv("MODE"), "O") { - fmt.Println("Run OTLP") - zipkins := NewOTLPSender(LogSenderOpts{ - ID: "traces", - Containers: names, - Lines: logs, - LinesPS: 40 * power, - URL: os.Getenv("URL"), - Headers: headers, - }) - zipkins.Run() - } - if strings.Contains(os.Getenv("MODE"), "G") { - fmt.Println("Run zipkin") - zipkins := NewSGSender(LogSenderOpts{ - ID: "traces", - Containers: names, - Lines: logs, - LinesPS: 10 * power, - URL: os.Getenv("URL"), - Headers: headers, - }) - zipkins.Run() - } - if strings.Contains(os.Getenv("MODE"), "D") { - fmt.Println("Run datadog") - datadogs := NewDatadogSender(LogSenderOpts{ - ID: "traces", - Containers: names, - Lines: logs, - LinesPS: 120 * power, - URL: os.Getenv("URL"), - Headers: headers, - }) - datadogs.Run() - } - if strings.Contains(os.Getenv("MODE"), "I") { - fmt.Println("Run influx") - influx := NewInfluxSender(LogSenderOpts{ - ID: "influx", - Containers: names, - Lines: logs, - LinesPS: 100 * power, - URL: os.Getenv("URL"), - Headers: headers, - }) - influx.Run() - } - if strings.Contains(os.Getenv("MODE"), "C") { - fmt.Println("Run consistency checker") - cons := NewJSONConsistencyChecker(LogSenderOpts{ - ID: "consistency-1", - Containers: names, - Lines: logs, - LinesPS: 300 * power, - URL: os.Getenv("URL"), - Headers: headers, - }) - cons.Run() - } - if strings.Contains(os.Getenv("MODE"), "T") { - fmt.Println("Run time sender") - pqt := NewTimeSender(LogSenderOpts{ - ID: "longtest-TIME", - Containers: names, - Lines: logs, - LinesPS: 10, - URL: os.Getenv("URL"), - Headers: headers, - }) - pqt.Run() - } - t := time.NewTicker(time.Second) - go func() { - for range t.C { - s := stats.Collect() - fmt.Printf("Ok requests: %d, Errors: %d, Failed: %d\n", s[REQ_OK], s[REQ_ERR], s[REQ_FAIL]) - fmt.Printf("Ok Requests time: min: %d, max: %d, avg: %f\n", - s[REQ_TIME_MS+"_min"], - s[REQ_TIME_MS+"_max"], - float64(s[REQ_TIME_MS+"_sum"])/float64(s[REQ_TIME_MS+"_count"])) - fmt.Printf("Ok Requests MB sent: %f, (%fMB/s)\n", - float64(s[REQ_BYTES+"_sum"])/1024/1024, - float64(s[REQ_BYTES+"_sum"])/1024/1024/5, - ) - } - }() - for { - time.Sleep(time.Second) - } -} - -func readTest() { - -} diff --git a/test/qryn_test_env/longtest/metricReader.go b/test/qryn_test_env/longtest/metricReader.go deleted file mode 100644 index 06ab7d0f..00000000 --- a/test/qryn_test_env/longtest/metricReader.go +++ /dev/null @@ -1 +0,0 @@ -package main diff --git a/test/qryn_test_env/longtest/metricSender.go b/test/qryn_test_env/longtest/metricSender.go deleted file mode 100644 index b52a3708..00000000 --- a/test/qryn_test_env/longtest/metricSender.go +++ /dev/null @@ -1,94 +0,0 @@ -package main - -import ( - "github.com/golang/protobuf/proto" - "github.com/golang/snappy" - "github.com/prometheus/prometheus/prompb" - "hash/crc32" - "math" - "math/rand" - "sync" - "time" -) - -type PromReq []prompb.TimeSeries - -func (p PromReq) Serialize() ([]byte, error) { - bytes, err := proto.Marshal(&prompb.WriteRequest{Timeseries: p}) - if err != nil { - return nil, err - } - enc := snappy.Encode(nil, bytes) - return enc, nil -} - -func NewMetricSender(opts LogSenderOpts) ISender { - var l *GenericSender - hdrs := opts.Headers - opts.Headers = map[string]string{} - for k, v := range hdrs { - opts.Headers[k] = v - } - opts.Headers["Content-Type"] = "application/x-protobuf" - opts.Headers["Content-Encoding"] = "snappy" - l = &GenericSender{ - LogSenderOpts: opts, - mtx: sync.Mutex{}, - rnd: rand.New(rand.NewSource(time.Now().UnixNano())), - timeout: time.Second * 15, - path: "/api/v1/prom/remote/write", - generate: func() IRequest { - if opts.LinesPS/3 < len(l.Containers) { - l.Containers = l.Containers[:opts.LinesPS/3] - } - req := make(PromReq, len(l.Containers)*3) - for i, container := range l.Containers { - base := int(crc32.ChecksumIEEE([]byte(container))) - req[i*3] = prompb.TimeSeries{ - Labels: []prompb.Label{ - {Name: "__name__", Value: "cpu_usage"}, - {Name: "container", Value: container}, - {Name: "orgid", Value: opts.Headers["X-Scope-OrgID"]}, - {Name: "sender", Value: "logmetrics"}, - }, - Samples: []prompb.Sample{ - { - Timestamp: time.Now().UnixMilli(), - Value: math.Max(float64(base%100+(l.random(20)-10)), 0), - }, - }, - } - req[i*3+1] = prompb.TimeSeries{ - Labels: []prompb.Label{ - {Name: "__name__", Value: "ram_usage"}, - {Name: "container", Value: container}, - {Name: "orgid", Value: opts.Headers["X-Scope-OrgID"]}, - {Name: "sender", Value: "logmetrics"}, - }, - Samples: []prompb.Sample{ - { - Timestamp: time.Now().UnixMilli(), - Value: math.Max(float64(base%1000+(l.random(200)-100)), 0), - }, - }, - } - req[i*3+2] = prompb.TimeSeries{ - Labels: []prompb.Label{ - {Name: "__name__", Value: "network_usage"}, - {Name: "container", Value: container}, - {Name: "orgid", Value: opts.Headers["X-Scope-OrgID"]}, - {Name: "sender", Value: "logmetrics"}, - }, - Samples: []prompb.Sample{ - { - Timestamp: time.Now().UnixMilli(), - Value: math.Max(float64(base%1000000+(l.random(2000)-1000)), 0), - }, - }, - } - } - return req - }, - } - return l -} diff --git a/test/qryn_test_env/longtest/otlpSender.go b/test/qryn_test_env/longtest/otlpSender.go deleted file mode 100644 index aeb22383..00000000 --- a/test/qryn_test_env/longtest/otlpSender.go +++ /dev/null @@ -1,132 +0,0 @@ -package main - -import ( - "fmt" - cv1 "go.opentelemetry.io/proto/otlp/common/v1" - rv1 "go.opentelemetry.io/proto/otlp/resource/v1" - v1 "go.opentelemetry.io/proto/otlp/trace/v1" - "google.golang.org/protobuf/proto" - "math/rand" - "os" - "time" - "unsafe" -) - -type OTLPReq v1.TracesData - -func (z *OTLPReq) Serialize() ([]byte, error) { - return proto.Marshal((*v1.TracesData)(z)) -} - -func NewOTLPSender(opts LogSenderOpts) ISender { - rnd := rand.New(rand.NewSource(time.Now().UnixNano())) - path := "/v1/traces" - if os.Getenv("ZPATH") != "" { - path = os.Getenv("ZPATH") - } - l := &GenericSender{ - LogSenderOpts: opts, - rnd: rnd, - timeout: time.Second, - path: path, - } - pickCont := func() string { - return l.pickRandom(opts.Containers) - } - /*pickLine := func() string { - return l.pickRandom(opts.Lines) - }*/ - l.generate = func() IRequest { - req := &OTLPReq{ - ResourceSpans: make([]*v1.ResourceSpans, opts.LinesPS/10), - } - for i := range req.ResourceSpans { - uintTraceId := []uint64{uint64(l.random(0xFFFFFFFF)), uint64(i)} - bTraceId := make([]byte, 16) - copy(bTraceId, unsafe.Slice((*byte)(unsafe.Pointer(&uintTraceId[0])), 16)) - uintSpanId := uint64(l.random(0xFFFFFFFF)) - bSpanId := make([]byte, 8) - copy(bSpanId, unsafe.Slice((*byte)(unsafe.Pointer(&uintSpanId)), 8)) - req.ResourceSpans[i] = &v1.ResourceSpans{ - Resource: &rv1.Resource{ - Attributes: []*cv1.KeyValue{ - { - Key: "service.name", - Value: &cv1.AnyValue{ - Value: &cv1.AnyValue_StringValue{ - StringValue: "longtest-service", - }, - }, - }, - { - Key: "sender", - Value: &cv1.AnyValue{ - Value: &cv1.AnyValue_StringValue{ - StringValue: "longtest", - }, - }, - }, - { - Key: "type", - Value: &cv1.AnyValue{ - Value: &cv1.AnyValue_StringValue{ - StringValue: "otlp", - }, - }, - }, - }, - DroppedAttributesCount: 0, - }, - ScopeSpans: []*v1.ScopeSpans{ - { - Spans: make([]*v1.Span, 10), - }, - }, - } - for j := range req.ResourceSpans[i].ScopeSpans[0].Spans { - kind := v1.Span_SPAN_KIND_CLIENT - if j%2 == 0 { - kind = v1.Span_SPAN_KIND_SERVER - } - req.ResourceSpans[i].ScopeSpans[0].Spans[j] = &v1.Span{ - TraceId: bTraceId, - SpanId: bSpanId, - ParentSpanId: nil, - Name: "longtest", - Kind: kind, - StartTimeUnixNano: uint64(time.Now(). - Add(time.Millisecond * time.Duration(-1*(l.random(500)))). - UnixNano()), - EndTimeUnixNano: uint64(time.Now().UnixNano()), - Attributes: []*cv1.KeyValue{ - { - Key: "container", - Value: &cv1.AnyValue{ - Value: &cv1.AnyValue_StringValue{ - StringValue: pickCont(), - }, - }, - }, - { - Key: "randomFloat", - Value: &cv1.AnyValue{ - Value: &cv1.AnyValue_StringValue{ - StringValue: fmt.Sprintf("%f", 50+(rand.Float64()*100-50)), - }, - }, - }, - }, - DroppedAttributesCount: 0, - Events: nil, - DroppedEventsCount: 0, - Links: nil, - DroppedLinksCount: 0, - Status: nil, - } - } - - } - return req - } - return l -} diff --git a/test/qryn_test_env/longtest/pqtSender.go b/test/qryn_test_env/longtest/pqtSender.go deleted file mode 100644 index 49b52fc4..00000000 --- a/test/qryn_test_env/longtest/pqtSender.go +++ /dev/null @@ -1,59 +0,0 @@ -package main - -import ( - "bytes" - "github.com/apache/arrow/go/v13/arrow" - _ "github.com/apache/arrow/go/v13/arrow" - "github.com/apache/arrow/go/v13/arrow/array" - "github.com/apache/arrow/go/v13/arrow/memory" - "github.com/apache/arrow/go/v13/parquet" - "github.com/apache/arrow/go/v13/parquet/pqarrow" - _ "github.com/apache/arrow/go/v13/parquet/pqarrow" - "math/rand" - "time" -) - -type PqtReq struct { - arrow.Record -} - -func (p *PqtReq) Serialize() ([]byte, error) { - defer p.Release() - buf := bytes.NewBuffer(make([]byte, 0, 1024)) - w, err := pqarrow.NewFileWriter(p.Schema(), buf, parquet.NewWriterProperties(), pqarrow.NewArrowWriterProperties()) - if err != nil { - return nil, err - } - err = w.Write(p) - if err != nil { - return nil, err - } - err = w.Close() - return buf.Bytes(), err -} - -func NewPqtSender(opts LogSenderOpts) ISender { - bld := array.NewRecordBuilder(memory.DefaultAllocator, arrow.NewSchema([]arrow.Field{ - {Name: "timestamp_ns", Type: arrow.PrimitiveTypes.Int64}, - {Name: "opaque_id", Type: arrow.BinaryTypes.String}, - {Name: "mos", Type: arrow.PrimitiveTypes.Float64}, - }, nil)) - - l := &GenericSender{ - LogSenderOpts: opts, - rnd: rand.New(rand.NewSource(time.Now().UnixNano())), - timeout: time.Second, - path: "/api/dedicated", - } - l.generate = func() IRequest { - for i := 0; i < opts.LinesPS; i++ { - bld.Field(0).(*array.Int64Builder).Append(time.Now().UnixNano()) - bld.Field(1).(*array.StringBuilder).Append(l.pickRandom(l.Containers)) - bld.Field(2).(*array.Float64Builder).Append(l.rnd.Float64() * 100) - } - return &PqtReq{ - Record: bld.NewRecord(), - } - } - return l -} diff --git a/test/qryn_test_env/longtest/sendStats.go b/test/qryn_test_env/longtest/sendStats.go deleted file mode 100644 index b1aa4a06..00000000 --- a/test/qryn_test_env/longtest/sendStats.go +++ /dev/null @@ -1,99 +0,0 @@ -package main - -import ( - "math" - "strings" - "sync" - "time" -) - -type Stats struct { - stats []map[string]int64 - mtx sync.Mutex -} - -var stats = func() *Stats { - res := &Stats{ - stats: make([]map[string]int64, 1), - } - res.stats[0] = make(map[string]int64, 10) - t := time.NewTicker(time.Second) - go func() { - for range t.C { - res.mtx.Lock() - res.stats = append(res.stats, make(map[string]int64, 10)) - if len(res.stats) > 5 { - res.stats = res.stats[1:] - } - res.mtx.Unlock() - } - }() - return res -}() - -func (s *Stats) getOrDefault2(m map[string]int64, name string, def int64) int64 { - if val, ok := m[name]; ok { - return val - } - return def -} - -func (s *Stats) getOrDefault(name string, def int64) int64 { - return s.getOrDefault2(s.stats[len(s.stats)-1], name, def) -} - -func (s *Stats) Inc(name string) { - s.mtx.Lock() - defer s.mtx.Unlock() - s.stats[len(s.stats)-1][name] = s.getOrDefault(name, 0) + 1 -} - -func (s *Stats) Observe(name string, val int64) { - s.mtx.Lock() - defer s.mtx.Unlock() - min := s.getOrDefault(name+"_min", math.MaxInt64) - if min > val { - min = val - } - max := s.getOrDefault(name+"_max", math.MinInt64) - if max < val { - max = val - } - count := s.getOrDefault(name+"_count", 0) + 1 - sum := s.getOrDefault(name+"_sum", 0) + val - idx := len(s.stats) - 1 - s.stats[idx][name+"_min"] = min - s.stats[idx][name+"_max"] = max - s.stats[idx][name+"_count"] = count - s.stats[idx][name+"_sum"] = sum -} - -func (s *Stats) Collect() map[string]int64 { - s.mtx.Lock() - defer s.mtx.Unlock() - res := make(map[string]int64, 10) - for _, stats := range s.stats { - for k, v := range stats { - if strings.HasSuffix(k, "_min") { - a := s.getOrDefault2(res, k, math.MaxInt64) - if a < v { - res[k] = a - } else { - res[k] = v - } - continue - } - if strings.HasSuffix(k, "_max") { - a := s.getOrDefault2(res, k, math.MinInt64) - if a > v { - res[k] = a - } else { - res[k] = v - } - continue - } - res[k] = s.getOrDefault2(res, k, 0) + v - } - } - return res -} diff --git a/test/qryn_test_env/longtest/serviceGraphSender.go b/test/qryn_test_env/longtest/serviceGraphSender.go deleted file mode 100644 index 0850fb99..00000000 --- a/test/qryn_test_env/longtest/serviceGraphSender.go +++ /dev/null @@ -1,95 +0,0 @@ -package main - -import ( - "github.com/openzipkin/zipkin-go/model" - "math/rand" - "net" - "os" - "time" -) - -func NewSGSender(opts LogSenderOpts) ISender { - rnd := rand.New(rand.NewSource(time.Now().UnixNano())) - path := "/tempo/spans" - if os.Getenv("ZPATH") != "" { - path = os.Getenv("ZPATH") - } - l := &GenericSender{ - LogSenderOpts: opts, - rnd: rnd, - timeout: time.Second, - path: path, - } - pickCont := func() string { - return l.pickRandom(opts.Containers[:5]) - } - l.generate = func() IRequest { - req := make(ZipkinReq, opts.LinesPS) - high := rnd.Uint64() - dur := uint64(rnd.Float64() * 1000) - - for i := 0; i < opts.LinesPS; i += 2 { - client := "test2-" + pickCont() - server := "test2-" + pickCont() - req[i] = model.SpanModel{ - SpanContext: model.SpanContext{ - TraceID: model.TraceID{ - High: high, - Low: uint64(i / 100), - }, - ID: model.ID(i + 1), - }, - Name: "longtest-SG", - Timestamp: time.Now(), - Duration: time.Duration(dur) * time.Microsecond, - Kind: model.Client, - LocalEndpoint: &model.Endpoint{ - ServiceName: client, - IPv4: net.IPv4(192, 168, 0, 1), - IPv6: nil, - Port: 8080, - }, - RemoteEndpoint: &model.Endpoint{ - ServiceName: server, - IPv4: net.IPv4(192, 168, 0, 2), - IPv6: nil, - Port: 8080, - }, - Tags: map[string]string{ - "sender": "longtest-SG", - }, - } - req[i+1] = model.SpanModel{ - SpanContext: model.SpanContext{ - TraceID: model.TraceID{ - High: high, - Low: uint64(i / 100), - }, - ID: model.ID(i + 2), - ParentID: &[]model.ID{model.ID(i + 1)}[0], - }, - Name: "longtest-SG", - Timestamp: time.Now(), - Duration: time.Duration(dur/2) * time.Microsecond, - Kind: model.Server, - LocalEndpoint: &model.Endpoint{ - ServiceName: server, - IPv4: net.IPv4(192, 168, 0, 2), - IPv6: nil, - Port: 8080, - }, - RemoteEndpoint: &model.Endpoint{ - ServiceName: client, - IPv4: net.IPv4(192, 168, 0, 1), - IPv6: nil, - Port: 8080, - }, - Tags: map[string]string{ - "sender": "longtest-SG", - }, - } - } - return req - } - return l -} diff --git a/test/qryn_test_env/longtest/stats.go b/test/qryn_test_env/longtest/stats.go deleted file mode 100644 index 30cd2bce..00000000 --- a/test/qryn_test_env/longtest/stats.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "sync" - "time" -) - -type Stat struct { - Timings map[string]int64 - mtx sync.Mutex -} - -func (s *Stat) AddTiming(timing time.Duration) { - s.mtx.Lock() - defer s.mtx.Unlock() - -} - -func (s *Stat) getOrDefault(k string, def int64) int64 { - if _, ok := s.Timings[k]; !ok { - return def - } - return s.Timings[k] -} diff --git a/test/qryn_test_env/longtest/timerSender.go b/test/qryn_test_env/longtest/timerSender.go deleted file mode 100644 index 2a4ef126..00000000 --- a/test/qryn_test_env/longtest/timerSender.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import ( - "github.com/prometheus/prometheus/prompb" - "math/rand" - "sync" - "time" -) - -func NewTimeSender(opts LogSenderOpts) ISender { - var l *GenericSender - hdrs := opts.Headers - opts.Headers = map[string]string{} - for k, v := range hdrs { - opts.Headers[k] = v - } - opts.Headers["Content-Type"] = "application/x-protobuf" - opts.Headers["Content-Encoding"] = "snappy" - l = &GenericSender{ - LogSenderOpts: opts, - mtx: sync.Mutex{}, - rnd: rand.New(rand.NewSource(time.Now().UnixNano())), - timeout: time.Second * 15, - path: "/api/v1/prom/remote/write", - generate: func() IRequest { - req := make(PromReq, l.LinesPS) - for i := 0; i < l.LinesPS; i++ { - container := l.Containers[i%len(l.Containers)] - req[i] = prompb.TimeSeries{ - Labels: []prompb.Label{ - {Name: "orgid", Value: opts.Headers["X-Scope-OrgID"]}, - {Name: "__name__", Value: "current_time"}, - {Name: "container", Value: container}, - {Name: "sender", Value: "logmetrics"}, - }, - Samples: []prompb.Sample{ - { - Timestamp: time.Now().UnixMilli(), - Value: float64(time.Now().Unix()), - }, - }, - } - } - return req - }, - } - return l -} diff --git a/test/qryn_test_env/longtest/zipkinSender.go b/test/qryn_test_env/longtest/zipkinSender.go deleted file mode 100644 index 94e48e26..00000000 --- a/test/qryn_test_env/longtest/zipkinSender.go +++ /dev/null @@ -1,72 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "github.com/openzipkin/zipkin-go/model" - "math/rand" - "net" - "os" - "time" -) - -type ZipkinReq []model.SpanModel - -func (z ZipkinReq) Serialize() ([]byte, error) { - return json.Marshal(z) -} - -func NewZipkinSender(opts LogSenderOpts) ISender { - rnd := rand.New(rand.NewSource(time.Now().UnixNano())) - path := "/tempo/spans" - if os.Getenv("ZPATH") != "" { - path = os.Getenv("ZPATH") - } - l := &GenericSender{ - LogSenderOpts: opts, - rnd: rnd, - timeout: time.Second, - path: path, - } - pickCont := func() string { - return l.pickRandom(opts.Containers) - } - pickLine := func() string { - return l.pickRandom(opts.Lines) - } - l.generate = func() IRequest { - req := make(ZipkinReq, opts.LinesPS) - high := rnd.Uint64() - - for i := 0; i < opts.LinesPS; i++ { - req[i] = model.SpanModel{ - SpanContext: model.SpanContext{ - TraceID: model.TraceID{ - High: high, - Low: uint64(i / 100), - }, - ID: model.ID(i + 1), - }, - Name: "longtest", - Timestamp: time.Now(), - Duration: 1000, - LocalEndpoint: &model.Endpoint{ - ServiceName: "longtest-service", - IPv4: net.IPv4(192, 168, 0, 1), - IPv6: nil, - Port: 8080, - }, - Annotations: []model.Annotation{ - {time.Now(), pickLine()}, - }, - Tags: map[string]string{ - "sender": "longtest", - "randomContainer": pickCont(), - "randomFloat": fmt.Sprintf("%f", 50+(rand.Float64()*100-50)), - }, - } - } - return req - } - return l -} diff --git a/test/qryn_test_env/otel/otel-collector-config.yaml b/test/qryn_test_env/otel/otel-collector-config.yaml deleted file mode 100644 index 27bed126..00000000 --- a/test/qryn_test_env/otel/otel-collector-config.yaml +++ /dev/null @@ -1,116 +0,0 @@ -receivers: - loki: - use_incoming_timestamp: true - protocols: - http: - endpoint: 0.0.0.0:3100 - grpc: - endpoint: 0.0.0.0:3200 - syslog: - protocol: rfc5424 - tcp: - listen_address: "0.0.0.0:5514" - fluentforward: - endpoint: 0.0.0.0:24224 - splunk_hec: - endpoint: 0.0.0.0:8088 - otlp: - protocols: - grpc: - endpoint: 0.0.0.0:4317 - http: - endpoint: 0.0.0.0:4318 - jaeger: - protocols: - grpc: - endpoint: 0.0.0.0:14250 - thrift_http: - endpoint: 0.0.0.0:14268 - zipkin: - endpoint: 0.0.0.0:9411 - skywalking: - protocols: - grpc: - endpoint: 0.0.0.0:11800 - http: - endpoint: 0.0.0.0:12800 - prometheus: - config: - scrape_configs: - - job_name: 'otel-collector' - scrape_interval: 5s - static_configs: - - targets: ['exporter:8080'] - - job_name: 'qryn' - scrape_interval: 5s - static_configs: - - targets: ['qryn:3100'] - influxdb: - endpoint: 0.0.0.0:8086 - pyroscopereceiver: - -processors: - batch: - send_batch_size: 10000 - timeout: 5s - memory_limiter: - check_interval: 2s - limit_mib: 1800 - spike_limit_mib: 500 - resourcedetection/system: - detectors: ['system'] - system: - hostname_sources: ['os'] - resource: - attributes: - - key: service.name - value: "serviceName" - action: upsert - -exporters: - qryn: - dsn: tcp://clickhouse-server:9000/qryn?username=qryn&password=demo - timeout: 10s - sending_queue: - queue_size: 100 - retry_on_failure: - enabled: true - initial_interval: 5s - max_interval: 30s - max_elapsed_time: 300s - logs: - format: raw - otlp: - endpoint: localhost:4317 - tls: - insecure: true - clickhouseprofileexporter: - dsn: clickhouse://clickhouse-server:9000/qryn?username=qryn&password=demo - -extensions: - health_check: - pprof: - zpages: - pyroscope: - -service: - telemetry: - logs: - level: "info" - extensions: [pprof, zpages, health_check, pyroscope] - pipelines: - logs: - receivers: [fluentforward, otlp, loki] - processors: [memory_limiter, resourcedetection/system, resource, batch] - exporters: [qryn] - traces: - receivers: [otlp, jaeger, zipkin, skywalking] - processors: [memory_limiter, resourcedetection/system, resource, batch] - exporters: [qryn] - metrics: - receivers: [prometheus, influxdb] - processors: [memory_limiter, resourcedetection/system, resource, batch] - exporters: [qryn] - logs/profiles: - receivers: [pyroscopereceiver] - exporters: [clickhouseprofileexporter] diff --git a/test/send.js b/test/send.js deleted file mode 100644 index caea57f0..00000000 --- a/test/send.js +++ /dev/null @@ -1,23 +0,0 @@ -const { createPoints, sendPoints } = require('./common') - -const period = 1000 // process.argv[1] -const time = 600000 // process.argv[2] -const id = '_TEST_' // process.argv[3]; - -let cnt = 0 - -async function main () { - console.log('started') - const t = setInterval(() => { - cnt++ - const points = createPoints(id, 1, Date.now() - 1000, Date.now(), {}, {}, - () => `MSG_${cnt}`) - sendPoints('http://localhost:3100', points) - }, period) - - await new Promise((resolve, reject) => setTimeout(resolve, time)) - clearInterval(t) - console.log('end') -} - -main() diff --git a/test/snap_rm.js b/test/snap_rm.js deleted file mode 100644 index a89e09e0..00000000 --- a/test/snap_rm.js +++ /dev/null @@ -1,6 +0,0 @@ -const { readFileSync, writeFileSync } = require('fs') - -let content = readFileSync(process.argv[2], { encoding: 'utf8' }) -const re = new RegExp(`^exports\\[\`${process.argv[3]}\`\\] = \`(\n([^e].*)$)+\n\nexports\\[`, 'gm') -content = content.replace(re, 'exports[') -writeFileSync(process.argv[2] + '_', content) diff --git a/test/tempo.sh b/test/tempo.sh deleted file mode 100755 index 4b9fe340..00000000 --- a/test/tempo.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -curl -X POST http://localhost:3100/tempo/api/push -H 'Content-Type: application/json' -d '[{ - "id": "1234", - "traceId": "d6e9329d67b6146a", - "timestamp": '$(date +%s%N | cut -b1-16)', - "duration": 100000, - "name": "span from bash!", - "tags": { - "http.method": "GET", - "http.path": "/api" - }, - "localEndpoint": { - "serviceName": "shell script" - } -}]' - -curl -X POST http://localhost:3100/tempo/api/push -H 'Content-Type: application/json' -d '[{ - "id": "5678", - "traceId": "d6e9329d67b6146a", - "parentId": "1234", - "timestamp": '$(date +%s%N | cut -b1-16)', - "duration": 100000, - "name": "child span from bash!", - "localEndpoint": { - "serviceName": "shell script" - } -}]' - -sleep 2 -curl http://localhost:3100/api/traces/d6e9329d67b6146a diff --git a/test/traceql_parser.test.js b/test/traceql_parser.test.js deleted file mode 100644 index 0a6c09a4..00000000 --- a/test/traceql_parser.test.js +++ /dev/null @@ -1,51 +0,0 @@ -const parser = require('../traceql/parser') - -it('traceql: one selector', () => { - const res = parser.ParseScript('{.testId="12345"}') - expect(res.rootToken.value).toEqual('{.testId="12345"}') -}) - -it('traceql: multiple selectors', () => { - const res = parser.ParseScript('{.testId="12345" &&.spanN=9}') - expect(res.rootToken.value).toEqual('{.testId="12345" &&.spanN=9}') -}) - -it('traceql: multiple selectors OR Brackets', () => { - const res = parser.ParseScript('{.testId="12345" && (.spanN=9 ||.spanN=8)}') - expect(res.rootToken.value).toEqual('{.testId="12345" && (.spanN=9 ||.spanN=8)}') -}) - -it('traceql: multiple selectors regexp', () => { - const res = parser.ParseScript('{.testId="12345" &&.spanN=~"(9|8)"}') - expect(res.rootToken.value).toEqual('{.testId="12345" &&.spanN=~"(9|8)"}') -}) - -it('traceql: duration', () => { - const res = parser.ParseScript('{.testId="12345" && duration>=9ms}') - expect(res.rootToken.value).toEqual('{.testId="12345" && duration>=9ms}') -}) - -it('traceql: float comparison', () => { - const res = parser.ParseScript('{.testId="12345" &&.spanN>=8.9}') - expect(res.rootToken.value).toEqual('{.testId="12345" &&.spanN>=8.9}') -}) - -it('traceql: count empty result', () => { - const res = parser.ParseScript('{.testId="12345" &&.spanN>=8.9} | count() > 1') - expect(res.rootToken.value).toEqual('{.testId="12345" &&.spanN>=8.9} | count() > 1') -}) - -it('traceql: max duration empty result', () => { - const res = parser.ParseScript('{.testId="12345" &&.spanN>=8.9} | max(duration) > 9ms') - expect(res.rootToken.value).toEqual('{.testId="12345" &&.spanN>=8.9} | max(duration) > 9ms') -}) - -it('traceql: max duration', () => { - const res = parser.ParseScript('{.testId="12345" &&.spanN>=8.9} | max(duration) > 8ms') - expect(res.rootToken.value).toEqual('{.testId="12345" &&.spanN>=8.9} | max(duration) > 8ms') -}) - -it('traceql: select', () => { - const res = parser.ParseScript('{.testId="12345" &&.spanN>=8.9} | select(a, b)') - expect(res.rootToken.value).toEqual('{.testId="12345" &&.spanN>=8.9} | select(a, b)') -}) diff --git a/test/transpiler.test.js b/test/transpiler.test.js deleted file mode 100644 index fc451999..00000000 --- a/test/transpiler.test.js +++ /dev/null @@ -1,387 +0,0 @@ -const bnf = require('../parser/bnf') -const transpiler = require('../parser/transpiler') -const { DataStream } = require('scramjet') -const UTILS = require('../lib/utils') -const { DATABASE_NAME, samplesReadTableName } = require('../lib/utils') -const { sharedParamNames } = require('../parser/registry/common') - -beforeAll(() => { - UTILS.samplesReadTableName = () => 'samples_v4' - process.env.CLICKHOUSE_DB = 'loki' -}) - -const setQueryParam = (query, name, val) => { - if (query.getParam(name)) { - query.getParam(name).set(val) - } -} - -jest.mock('../lib/utils', () => { - const originalModule = jest.requireActual('../lib/utils') - return { - ...originalModule, - samplesReadTableName: jest.fn(() => 'samples_vX') - } -}) - -const setParams = (query) => { - setQueryParam(query, sharedParamNames.timeSeriesTable, `${DATABASE_NAME()}.time_series`) - setQueryParam(query, sharedParamNames.samplesTable, `${DATABASE_NAME()}.${samplesReadTableName()}`) - setQueryParam(query, sharedParamNames.from, 1) - setQueryParam(query, sharedParamNames.to, 2) - setQueryParam(query, sharedParamNames.limit, 3) -} - -it('should transpile log_stream_selector', () => { - let scr = '{et_dolorem=`nemo doloremque`, quia="eum voluptatem non eligendi"}' - let script = bnf.ParseScript(scr) - let query = transpiler.transpileLogStreamSelector(script.rootToken, transpiler.initQuery()) - setParams(query) - expect(query).toMatchSnapshot() - expect(query.toString()).toMatchSnapshot() - - scr = '{rerum_laborum=~`^con.+q.at[a-z]r`}' - script = bnf.ParseScript(scr) - query = transpiler.transpileLogStreamSelector(script.rootToken, transpiler.initQuery()) - setParams(query) - expect(query).toMatchSnapshot() - expect(query.toString()).toMatchSnapshot() - - scr = '{et_dolorem!=`nemo doloremque`}' - script = bnf.ParseScript(scr) - query = transpiler.transpileLogStreamSelector(script.rootToken, transpiler.initQuery()) - setParams(query) - expect(query).toMatchSnapshot() - expect(query.toString()).toMatchSnapshot() - - scr = '{rerum_laborum!~`^con.+q.at[a-z]r`}' - script = bnf.ParseScript(scr) - query = transpiler.transpileLogStreamSelector(script.rootToken, transpiler.initQuery()) - setParams(query) - expect(query).toMatchSnapshot() - expect(query.toString()).toMatchSnapshot() -}) - -it('should transpile log_stream_selector with stream filter', () => { - let scr = '{et_dolorem=`nemo doloremque`, quia="eum voluptatem non eligendi"} |= "at et"' - let script = bnf.ParseScript(scr) - let query = transpiler.transpileLogStreamSelector(script.rootToken, transpiler.initQuery()) - setParams(query) - expect(query).toMatchSnapshot() - expect(query.toString()).toMatchSnapshot() - - scr = '{rerum_laborum=~`^con.+q.at[a-z]r`} != "consequatur nam soluta"' - script = bnf.ParseScript(scr) - query = transpiler.transpileLogStreamSelector(script.rootToken, transpiler.initQuery()) - setParams(query) - expect(query).toMatchSnapshot() - expect(query.toString()).toMatchSnapshot() - - scr = '{et_dolorem!=`nemo doloremque`} |~ "^mol[eE][^ ]+e +voluptatibus"' - script = bnf.ParseScript(scr) - query = transpiler.transpileLogStreamSelector(script.rootToken, transpiler.initQuery()) - setParams(query) - expect(query).toMatchSnapshot() - expect(query.toString()).toMatchSnapshot() - - scr = '{rerum_laborum!~`^con.+q.at[a-z]r`} !~ "cons[eE][^ ]+r nam soluta"' - script = bnf.ParseScript(scr) - query = transpiler.transpileLogStreamSelector(script.rootToken, transpiler.initQuery()) - setParams(query) - expect(query).toMatchSnapshot() - expect(query.toString()).toMatchSnapshot() -}) -describe('log_range_aggregation', () => { - const test = (scr) => { - const script = bnf.ParseScript(scr) - const q = transpiler.initQuery() - q.ctx.step = 5000 - const query = transpiler.transpileLogRangeAggregation(script.rootToken, q) - setParams(query) - expect(query).toMatchSnapshot() - expect(query.toString()).toMatchSnapshot() - } - it('1', () => { - test('rate({minus_nam="aut illo"}[5m])') - }) - it('2', () => test('rate({rerum_laborum=~`^con.+q.at[a-z]r`} != "consequatur nam soluta" [5m])')) - it('3', () => test('rate({et_dolorem!=`nemo doloremque`} |~ "^mol[eE][^ ]+e +voluptatibus" [5m])')) - it('4', () => test('rate({rerum_laborum!~`^con.+q.at[a-z]r`} !~ "cons[eE][^ ]+r nam soluta" [1s])')) -}) - -it('should transpile aggregation_operator', () => { - let scr = 'sum (rate({minus_nam="aut illo"}[5m])) by (label_1)' - let script = bnf.ParseScript(scr) - let q = transpiler.initQuery() - q.ctx = { - start: 0, - end: 3600 * 1000 - } - let query = transpiler.transpileAggregationOperator(script.rootToken, q) - setParams(query) - expect(query).toMatchSnapshot() - expect(query.toString()).toMatchSnapshot() - - scr = 'sum by (label_1) (rate({rerum_laborum=~`^con.+q.at[a-z]r`} != "consequatur nam soluta" [5m]))' - script = bnf.ParseScript(scr) - query = transpiler.transpileAggregationOperator(script.rootToken, q) - setParams(query) - expect(query).toMatchSnapshot() - expect(query.toString()).toMatchSnapshot() - - scr = 'sum by (label_1) (rate({minus_nam="aut illo"}[5m]))' - script = bnf.ParseScript(scr) - q = transpiler.initQuery() - q.ctx = { - start: 0, - end: 3600 * 1000 - } - query = transpiler.transpileAggregationOperator(script.rootToken, q) - setParams(query) - expect(query).toMatchSnapshot() - expect(query.toString()).toMatchSnapshot() - - /* scr = 'rate({et_dolorem!=`nemo doloremque`} |~ "^mol[eE][^ ]+e +voluptatibus" [5m])'; - script = bnf.ParseScript(scr); - query = transpiler.transpileAggregationOperator(script.rootToken, transpiler.initQuery()); - expect(query).toMatchSnapshot(); - expect(query.toString()).toMatchSnapshot(); - - scr = 'rate({rerum_laborum!~`^con.+q.at[a-z]r`} !~ "cons[eE][^ ]+r nam soluta" [5m])'; - script = bnf.ParseScript(scr); - query = transpiler.transpileAggregationOperator(script.rootToken, transpiler.initQuery()); - expect(query).toMatchSnapshot(); - expect(query.toString()).toMatchSnapshot(); */ -}) - -it('should transpile json requests', async () => { - let script = bnf.ParseScript('{autem_quis="quidem sit"}| json odit_iusto="dicta"') - let res = transpiler.transpileLogStreamSelector(script.rootToken, transpiler.initQuery()) - setParams(res) - expect(res).toMatchSnapshot() - script = bnf.ParseScript('{autem_quis="quidem sit"}| json') - res = transpiler.transpileLogStreamSelector(script.rootToken, transpiler.initQuery()) - let stream = DataStream.from([{ - labels: { autem_quis: 'quidem sit', l1: 'v1', l2: 'v2' }, - string: JSON.stringify({ l1: 'v3', l3: 'v4' }) - }]) - res.ctx.stream.forEach(f => { stream = f(stream) }) - res = await stream.toArray() - expect(res).toMatchSnapshot() -}) - -it('should transpile logfmt requests', async () => { - const script = bnf.ParseScript('{autem_quis="quidem sit"}| logfmt') - let res = transpiler.transpileLogStreamSelector(script.rootToken, transpiler.initQuery()) - let stream = DataStream.from([{ - labels: { autem_quis: 'quidem sit', l1: 'v1', l2: 'v2' }, - string: 'l1="v3" l3="v4" ' - }]) - res.ctx.stream.forEach(f => { stream = f(stream) }) - res = await stream.toArray() - expect(res).toMatchSnapshot() -}) - -it('shoud transpile unwrap', async () => { - let q = transpiler.initQuery() - q.ctx.step = 120000 - let script = bnf.ParseScript('rate({test_id="0.7857680014573265_json"}| unwrap int_lbl [1m]) by (fmt)') - expect(script).toBeTruthy() - q = transpiler.initQuery() - q.ctx.step = 120000 - let req = transpiler.transpileUnwrapFunction(script.rootToken, q) - expect(req).toMatchSnapshot() - - script = bnf.ParseScript('rate({test_id="0.7857680014573265_json"}| json int_lbl2="int_val"| unwrap int_lbl2 [1m]) by (fmt)') - q = transpiler.initQuery() - q.ctx.step = 120000 - req = transpiler.transpileUnwrapFunction(script.rootToken, q) - expect(req).toMatchSnapshot() - script = bnf.ParseScript('rate({test_id="0.7857680014573265_json"}| json int_lbl2="int_val"| unwrap int_lbl [1m]) by (int_lbl2)') - q = transpiler.initQuery() - q.ctx.step = 120000 - req = transpiler.transpileUnwrapFunction(script.rootToken, q) - expect(req).toMatchSnapshot() - - const testData = [{ - timestamp_ns: 0, - labels: { test_id: '0.7857680014573265_json', freq: '1', fmt: 'json', lbl_repl: 'val_repl', int_lbl: '1' }, - string: JSON.stringify({ lbl_repl: 'REPL', int_val: '1', new_lbl: 'new_val', str_id: 0, arr: [1, 2, 3], obj: { o_1: 'v_1' } }) - }, { - timestamp_ns: 1000, - labels: { test_id: '0.7857680014573265_json', freq: '1', fmt: 'json', lbl_repl: 'val_repl', int_lbl: '1' }, - string: JSON.stringify({ lbl_repl: 'REPL', int_val: '1', new_lbl: 'new_val', str_id: 0, arr: [1, 2, 3], obj: { o_1: 'v_1' } }) - }, { - timestamp_ns: 2000, - labels: { test_id: '0.7857680014573265_json', freq: '1', fmt: 'json', lbl_repl: 'val_repl', int_lbl: '1' }, - string: JSON.stringify({ lbl_repl: 'REPL', int_val: 'ewew', new_lbl: 'new_val', str_id: 0, arr: [1, 2, 3], obj: { o_1: 'v_1' } }) - }, { EOF: true }] - script = bnf.ParseScript('sum_over_time({test_id="0.7857680014573265_json"}| json| unwrap int_val [2s]) by (freq)') - q = transpiler.initQuery() - q.ctx.step = 1000 - req = transpiler.transpileUnwrapFunction(script.rootToken, q) - let ds = DataStream.fromArray(testData) - req.ctx.stream.forEach(s => { - ds = s(ds) - }) - const res = await ds.toArray() - expect(res).toEqual([{ labels: { freq: '1' }, timestamp_ns: '0', value: 2 }, { EOF: true }]) - - expect(() => transpiler.transpile({ query: 'rate({test_id="1"} |~ "123" | unwrap_value [1s])' })) - .toThrowError('log pipeline not supported') - - /* expect(res).toMatchSnapshot(); - script = bnf.ParseScript(`{test_id="0.7857680014573265_json"}| json| unwrap int_lbl`); - req = transpiler.transpile_unwrap_expression(script.rootToken, transpiler.initQuery()); - ds = DataStream.fromArray(testData); - req.stream.forEach(s => { - ds = s(ds); - }); - res = await ds.toArray(); - expect(res).toMatchSnapshot(); */ -}) - -it('should transpile complex pipelines', async () => { - const q = transpiler.transpile({ - query: '{test_id="${testID}"} | freq >= 4', - limit: 1000, - direction: 'forward', - start: '1', - end: '100000000000000', - step: 1, - stream: [] - }) - expect(q).toMatchSnapshot() -}) - -it('should transpile line format', async () => { - let script = bnf.ParseScript('{a="b"} | line_format "{{_entry}} {{lbl1}} {{divide int 2}}"') - let q = transpiler.transpileLogStreamSelector(script.rootToken, transpiler.initQuery()) - let ds = DataStream.fromArray([{ labels: { lbl1: 'a', int: 10 }, string: 'str' }]) - q.ctx.stream.forEach(s => { ds = s(ds) }) - expect(await ds.toArray()).toMatchSnapshot() - script = bnf.ParseScript('{a="b"} | line_format "{ \\"entry\\": \\"{{_entry}}\\", \\"intval\\": {{divide int 2}} }" | json') - q = transpiler.transpileLogStreamSelector(script.rootToken, transpiler.initQuery()) - ds = DataStream.fromArray([{ labels: { lbl1: 'a', int: 10 }, string: 'str' }]) - q.ctx.stream.forEach(s => { ds = s(ds) }) - expect(await ds.toArray()).toMatchSnapshot() - - q = transpiler.initQuery() - q.ctx.step = 1000 - script = bnf.ParseScript('rate({a="b"} | line_format "{ \\"entry\\": \\"{{_entry}}\\", \\"intval\\": {{divide int 2}} }" | json | unwrap intval [1s])') - q = transpiler.transpileUnwrapFunction(script.rootToken, q) - ds = DataStream.fromArray([{ labels: { lbl1: 'a', int: 10 }, timestamp_ns: 0, string: 'str' }, { EOF: true }]) - q.ctx.stream.forEach(s => { ds = s(ds) }) - expect(await ds.toArray()).toMatchSnapshot() - // console.log(await ds.toArray()); -}) - -it('should transpile plugins', async () => { - const script = bnf.ParseScript('derivative({a="b"} | unwrap int [10s])') - const _q = transpiler.initQuery() - _q.ctx.step = 1000 - const q = transpiler.transpileUnwrapFunction(script.rootToken, _q) - let ds = DataStream.fromArray([ - { labels: { lbl1: 'a' }, unwrapped: 10, timestamp_ns: 0, string: 'str' }, - { labels: { lbl1: 'a' }, unwrapped: 20, timestamp_ns: 1000, string: 'str' }, - { labels: { lbl1: 'a' }, unwrapped: 30, timestamp_ns: 2000, string: 'str' }, - { EOF: true } - ]) - q.ctx.stream.forEach(s => { ds = s(ds) }) - expect(await ds.toArray()).toMatchSnapshot() -}) - -it('should transpile macro', async () => { - const script = bnf.ParseScript('test_macro("b")') - expect(transpiler.transpileMacro(script.rootToken.Child('user_macro'))) - .toMatch('{test_id="b"}') -}) - -describe('should transpile new style', () => { - const cq = (q) => ({ - direction: 'BACKWARD', - limit: '2000', - query: q, - start: '1638802620000000000', - end: '1638803220000000000', - step: '2' - }) - it('1', () => { - const res = transpiler.transpile(cq('{test_id=\"0.7387779420506657\"}')) - expect(res).toMatchSnapshot() - }) - it('2', () => { - const res = transpiler.transpile(cq('{test_id=\"0.2119268970232\", freq=\"2\"} |~ \"2[0-9]$\"')) - expect(res).toMatchSnapshot() - }) - it('3', () => { - const res = transpiler.transpile(cq('rate({test_id=\"0.7026038163617259\", freq=\"2\"} |~ \"2[0-9]$\" [1s])')) - expect(res).toMatchSnapshot() - }) - it('4', () => { - const res = transpiler.transpile(cq( - 'absent_over_time({test_id=\"0.7026038163617259\", freq=\"2\"} |~ \"2[0-9]$\" [1s])' - )) - expect(res).toMatchSnapshot() - }) - it('5', () => { - const res = transpiler.transpile(cq('{test_id="0.000341166036469831_json"}|json')) - expect(res).toMatchSnapshot() - }) - it('6', () => { - const res = transpiler.transpile(cq( - '{test_id=\"0.2053747382122484_json\"}|json lbl_repl=\"new_lbl\"|lbl_repl=\"new_val\"' - )) - expect(res).toMatchSnapshot() - }) - it('7', () => { - const res = transpiler.transpile(cq( - 'sum_over_time({test_id=\"0.1547558751138609_json\"}|json|lbl_repl=\"REPL\"|unwrap int_lbl [3s]) by (test_id, lbl_repl)' - )) - expect(res).toMatchSnapshot() - }) - it('8', () => { - const res = transpiler.transpile(cq( - 'rate({test_id=\"0.4075242197275857\"}| line_format \"{ \\\"str\\\":\\\"{{_entry}}\\\", \\\"freq2\\\": {{divide freq 2}} }\"| json|unwrap freq2 [1s]) by (test_id, freq2)' - )) - expect(res).toMatchSnapshot() - }) - it('9', () => { - const res = transpiler.transpile(cq( - '{test_id=\"0.7186063017626447_json\"} | json sid=\"str_id\" | sid >= 598' - )) - expect(res).toMatchSnapshot() - }) - it('10', () => { - const res = transpiler.transpile(cq( - '{test_id=\"0.5505504081219323\"} | regexp \"^(?<e>[^0-9]+)[0-9]+$\"' - )) - expect(res).toMatchSnapshot() - }) - it('11', () => { - const res = transpiler.transpile(cq( - 'rate({label="val"} | unwrap b [1s])' - )) - expect(res).toMatchSnapshot() - }) - it('12', () => { - const res = transpiler.transpile(cq( - '{freq="1"} | line_format "1x" | regexp "^(?<e>.)x"' - )) - expect(res).toMatchSnapshot() - }) -}) - -it('should transpile tail', () => { - const res = transpiler.transpileTail({ query: '{test_id=~"_ws"}' }) - expect(res).toMatchSnapshot() -}) - -it('should transpile series', () => { - const res = transpiler.transpileSeries(['{test_id="123"}']) - expect(res).toMatchSnapshot() -}) - -it('should transpile summary', () => { - console.log(transpiler.transpile({query: 'summary({sender="logtest"})'}).query) -}) diff --git a/traceql/clickhouse_transpiler/aggregator.js b/traceql/clickhouse_transpiler/aggregator.js deleted file mode 100644 index 5348b8fd..00000000 --- a/traceql/clickhouse_transpiler/aggregator.js +++ /dev/null @@ -1,103 +0,0 @@ -const Sql = require('@cloki/clickhouse-sql') -const { getCompareFn, durationToNs } = require('./shared') - -module.exports = class Builder { - constructor () { - this.main = null - this.fn = '' - this.attr = '' - this.compareFn = '' - this.compareVal = '' - } - - /** - * - * @param main {BuiltProcessFn} - * @returns {Builder} - */ - withMain (main) { - this.main = main - return this - } - - /** - * - * @param fn {string} - * @returns {Builder} - */ - withFn (fn) { - this.fn = fn - return this - } - - /** - * - * @param attr {string} - * @returns {Builder} - */ - withAttr (attr) { - this.attr = attr - return this - } - - /** - * - * @param fn {string} - * @returns {Builder} - */ - withCompareFn (fn) { - this.compareFn = fn - return this - } - - /** - * - * @param val {string} - * @returns {Builder} - */ - withCompareVal (val) { - this.compareVal = val - return this - } - - /** - * @returns {ProcessFn} - */ - build () { - const self = this - /** @type {BuiltProcessFn} */ - const res = (ctx) => { - const sel = this.main(ctx) - const fCmpVal = self.cmpVal() - const agg = self.aggregator() - const compareFn = getCompareFn(self.compareFn) - const comparreExp = compareFn(agg, Sql.val(fCmpVal)) - // .having is broken - sel.having_conditions = Sql.And([...sel.having_conditions.args, comparreExp]) - return sel - } - return res - } - - cmpVal () { - if (this.attr === 'duration') { - return durationToNs(this.compareVal) - } - return parseFloat(this.compareVal) - } - - aggregator () { - switch (this.fn) { - case 'count': - return new Sql.Raw('toFloat64(count(distinct index_search.span_id))') - case 'avg': - return new Sql.Raw('avgIf(agg_val, isNotNull(agg_val))') - case 'max': - return new Sql.Raw('maxIf(agg_val, isNotNull(agg_val))') - case 'min': - return new Sql.Raw('minIf(agg_val, isNotNull(agg_val))') - case 'sum': - return new Sql.Raw('sumIf(agg_val, isNotNull(agg_val))') - } - } -} diff --git a/traceql/clickhouse_transpiler/attr_condition.js b/traceql/clickhouse_transpiler/attr_condition.js deleted file mode 100644 index d9a709e4..00000000 --- a/traceql/clickhouse_transpiler/attr_condition.js +++ /dev/null @@ -1,317 +0,0 @@ -const { getCompareFn, durationToNs, unquote, bitSet } = require('./shared') -const Sql = require('@cloki/clickhouse-sql') -module.exports = class Builder { - constructor () { - this.main = null - this.precondition = null - this.terms = [] - this.conds = null - this.aggregatedAttr = '' - - this.sqlConditions = [] - this.isAliased = false - this.alias = '' - this.where = [] - } - - /** - * - * @param main {BuiltProcessFn} - * @returns {Builder} - */ - withMain (main) { - this.main = main - return this - } - - /** - * @param terms {[]} - * @returns {Builder} - */ - withTerms (terms) { - this.terms = terms - return this - } - - /** - * @param conds - * @returns {Builder} - */ - withConditions (conds) { - this.conds = conds - return this - } - - /** - * - * @param aggregatedAttr {string} - * @returns {Builder} - */ - withAggregatedAttr (aggregatedAttr) { - this.aggregatedAttr = aggregatedAttr - return this - } - - withPrecondition (precondition) { - this.precondition = precondition - return this - } - - /** - * @returns {ProcessFn} - */ - build () { - const self = this - /** @type {BuiltProcessFn} */ - const res = (ctx) => { - const sel = self.main(ctx) - const withPreconditionSel = self.precondition - ? new Sql.With('precond', self.buildPrecondition(ctx)) - : null - self.alias = 'bsCond' - for (const term of self.terms) { - const sqlTerm = self.getTerm(term) - self.sqlConditions.push(sqlTerm) - if (!term.Child('label_name').value.match(/^(\.|span\.|resource\.|name|status)/)) { - continue - } - self.where.push(sqlTerm) - } - const having = self.getCond(self.conds) - self.aggregator(sel) - sel.conditions = Sql.And(sel.conditions, Sql.Or(...self.where)) - if (Array.isArray(ctx.randomFilter) && Array.isArray(ctx.cachedTraceIds) && ctx.cachedTraceIds.length > 0) { - sel.conditions = Sql.And( - sel.conditions, - Sql.Or( - Sql.Eq(new Sql.Raw(`cityHash64(trace_id) % ${ctx.randomFilter[0]}`), Sql.val(ctx.randomFilter[1])), - new Sql.In('trace_id', 'in', ctx.cachedTraceIds.map(traceId => new Sql.Raw(`unhex('${traceId}')`))) - )) - } else if (Array.isArray(ctx.randomFilter)) { - sel.conditions = Sql.And( - sel.conditions, - Sql.Eq(new Sql.Raw(`cityHash64(trace_id) % ${ctx.randomFilter[0]}`), Sql.val(ctx.randomFilter[1]))) - } - if (withPreconditionSel) { - sel.with(withPreconditionSel) - sel.conditions = Sql.And( - sel.conditions, - new Sql.In(new Sql.Raw('(trace_id, span_id)'), 'in', new Sql.WithReference(withPreconditionSel))) - } - sel.having(having) - return sel - } - return res - } - - buildPrecondition (ctx) { - if (!this.precondition) { - return null - } - const sel = this.precondition(ctx) - sel.select_list = sel.select_list.filter(x => Array.isArray(x) && (x[1] === 'trace_id' || x[1] === 'span_id')) - sel.order_expressions = [] - return sel - } - - /** - * @typedef {{simpleIdx: number, op: string, complex: [Condition]}} Condition - */ - /** - * @param c {Token || [any]} - */ - getCond (c) { - if (c.name) { - let left = new Sql.Raw(this.alias) - if (!this.isAliased) { - left = groupBitOr(bitSet(this.sqlConditions), this.alias) - } - const termIdx = this.terms.findIndex(x => x.value === c.value) - return Sql.Ne(bitAnd(left, new Sql.Raw((BigInt(1) << BigInt(termIdx)).toString())), Sql.val(0)) - } - const op = c[0] - const subs = c.slice(1).map(x => this.getCond(x)) - switch (op) { - case '&&': - return Sql.And(...subs) - case '||': - return Sql.Or(...subs) - } - throw new Error(`unsupported condition operator ${c.op}`) - } - - /** - * @param sel {Select} - */ - aggregator (sel) { - if (!this.aggregatedAttr) { - return - } - - if (this.aggregatedAttr === 'duration') { - sel.select([new Sql.Raw('toFloat64(any(traces_idx.duration))'), 'agg_val']) - return - } - - if (this.aggregatedAttr.match(/^span./)) { - this.aggregatedAttr = this.aggregatedAttr.substr(5) - } - if (this.aggregatedAttr.match(/^resource\./)) { - this.aggregatedAttr = this.aggregatedAttr.substr(9) - } - if (this.aggregatedAttr.match(/^\./)) { - this.aggregatedAttr = this.aggregatedAttr.substr(1) - } - sel.select([sqlAttrValue(this.aggregatedAttr), 'agg_val']) - this.where.push(Sql.Eq(new Sql.Raw('key'), Sql.val(this.aggregatedAttr))) - } - - getTerm (term) { - let key = term.Child('label_name').value - if (key.match(/^span\./)) { - key = key.substr(5) - } else if (key.match(/^resource\./)) { - key = key.substr(9) - } else if (key.match(/^\./)) { - key = key.substr(1) - } else { - switch (key) { - case 'duration': - return this.getDurationCondition(key, term) - case 'name': - key = 'name' - break - case 'status': - key = 'status' - break - default: - throw new Error(`unsupported attribute ${key}`) - } - } - if (term.Child('quoted_str')) { - return this.getStrCondition(key, term) - } else if (term.Child('number')) { - return this.getNumberCondition(key, term) - } - throw new Error(`unsupported term statement ${term.value}`) - } - - getDurationCondition (key, term) { - const fVal = durationToNs(term.Child('value').value) - const fn = getCompareFn(term.Child('op').value) - return fn(new Sql.Raw('traces_idx.duration'), Math.floor(fVal)) - } - - getStrCondition (key, term) { - const strVal = this.getString(term) - switch (term.Child('op').value) { - case '=': - return Sql.And( - Sql.Eq(new Sql.Raw('key'), Sql.val(key)), - Sql.Eq(new Sql.Raw('val'), Sql.val(strVal)) - ) - case '!=': - return Sql.And( - Sql.Eq(new Sql.Raw('key'), Sql.val(key)), - Sql.Ne(new Sql.Raw('val'), Sql.val(strVal)) - ) - case '=~': - return Sql.And( - Sql.Eq(new Sql.Raw('key'), Sql.val(key)), - Sql.Eq(new Sql.Raw(`match(val, ${Sql.quoteVal(strVal)})`), 1) - ) - case '!~': - return Sql.And( - Sql.Eq(new Sql.Raw('key'), Sql.val(key)), - Sql.Ne(new Sql.Raw(`match(val, ${Sql.quoteVal(strVal)})`), 1) - ) - } - throw new Error(`unsupported term statement ${term.value}`) - } - - getNumberCondition (key, term) { - const fn = getCompareFn(term.Child('op').value) - if (!term.Child('value').value.match(/^\d+.?\d*$/)) { - throw new Error(`invalid value in ${term.value}`) - } - const fVal = parseFloat(term.Child('value').value) - return Sql.And( - Sql.Eq('key', Sql.val(key)), - Sql.Eq(new Sql.Raw('isNotNull(toFloat64OrNull(val))'), 1), - fn(new Sql.Raw('toFloat64OrZero(val)'), fVal) - ) - } - - getString (term) { - if (term.Child('quoted_str').value) { - return unquote(term.Child('quoted_str').value) - } - if (term.Child('number').value) { - return term.Child('number').value - } - throw new Error(`unsupported term statement ${term.value}`) - } -} - -/** - * - * @param left - * @param right - * @returns {SQLObject} - */ -function bitAnd (left, right) { - const res = new Sql.Raw('') - res.toString = () => { - const strLeft = left.toString() - const strRight = right.toString() - return `bitAnd(${strLeft}, ${strRight})` - } - return res -} - -/** - * - * @param left - * @param alias - * @returns {SQLObject} - */ -function groupBitOr (left, alias) { - const res = new Sql.Raw('') - res.toString = () => { - const strLeft = left.toString() - if (alias) { - return `groupBitOr(${strLeft}) as ${alias}` - } - return `groupBitOr(${strLeft})` - } - return res -} - -/** - * - * @param attr {string} - * @returns {SQLObject} - */ -function sqlAttrValue (attr) { - const res = new Sql.Raw('') - res.toString = () => { - const strAttr = Sql.quoteVal(attr) - return `anyIf(toFloat64OrNull(val), key == ${strAttr})` - } - return res -} - -/** - * type sqlAttrValue struct { - * attr string - * } - * - * func (s *sqlAttrValue) String(ctx *sql.Ctx, options ...int) (string, error) { - * attr, err := sql.NewStringVal(s.attr).String(ctx, options...) - * if err != nil { - * return "", err - * } - * - * return fmt.Sprintf("anyIf(toFloat64OrNull(val), key == %s)", attr), nil - * } - */ \ No newline at end of file diff --git a/traceql/clickhouse_transpiler/attr_condition_eval.js b/traceql/clickhouse_transpiler/attr_condition_eval.js deleted file mode 100644 index 2512ebc0..00000000 --- a/traceql/clickhouse_transpiler/attr_condition_eval.js +++ /dev/null @@ -1,19 +0,0 @@ -const attrCondition = require('./attr_condition') -const { bitSet } = require('./shared') -const Sql = require('@cloki/clickhouse-sql') -module.exports = class Builder extends attrCondition { - build () { - const self = this - const superBuild = super.build() - /** @type {BuiltProcessFn} */ - const res = (ctx) => { - const sel = superBuild(ctx) - sel.having_conditions = [] - sel.aggregations = [bitSet(self.sqlConditions)] - sel.select_list = [[bitSet(self.sqlConditions), 'cond'], [new Sql.Raw('count()'), 'count']] - sel.order_expressions = [] - return sel - } - return res - } -} diff --git a/traceql/clickhouse_transpiler/group_by.js b/traceql/clickhouse_transpiler/group_by.js deleted file mode 100644 index cda774b8..00000000 --- a/traceql/clickhouse_transpiler/group_by.js +++ /dev/null @@ -1,14 +0,0 @@ -const Sql = require('@cloki/clickhouse-sql') -const { standardBuilder } = require('./shared') - -module.exports = standardBuilder((sel, ctx) => { - const withMain = new Sql.With('index_search', sel) - return (new Sql.Select()) - .with(withMain) - .select( - ['trace_id', 'trace_id'], - [new Sql.Raw('groupArray(100)(span_id)'), 'span_id'] - ).from(new Sql.WithReference(withMain)) - .groupBy('trace_id') - .orderBy([new Sql.Raw('max(index_search.timestamp_ns)'), 'desc']) -}) diff --git a/traceql/clickhouse_transpiler/index.js b/traceql/clickhouse_transpiler/index.js deleted file mode 100644 index b8338a00..00000000 --- a/traceql/clickhouse_transpiler/index.js +++ /dev/null @@ -1,303 +0,0 @@ -const AttrConditionPlanner = require('./attr_condition') -const AttrConditionEvalPlanner = require('./attr_condition_eval') -const InitIndexPlanner = require('./init') -const IndexGroupByPlanner = require('./group_by') -const AggregatorPlanner = require('./aggregator') -const IndexLimitPlanner = require('./limit') -const TracesDataPlanner = require('./traces_data') -const { th } = require('date-fns/locale') - -/** - * @param script {Token} - */ -module.exports.transpile = (script) => { - return new module.exports.Planner(script).plan() -} - -/** - * @param script {Token} - */ -module.exports.evaluateCmpl = (script) => { - return new module.exports.Planner(script).planEval() -} - -module.exports.Planner = class Planner { - /** - * - * @param script {Token} - */ - constructor (script) { - this.script = script - this.cond = null - this.terms = {} - this.termIdx = [] - - this.eval = null - - this.preCond = null - this.preCondTerms = {} - this.precondTermsIdx = [] - - this.aggregatedAttr = '' - this.cmpVal = '' - - this.aggFn = '' - } - - plan () { - this.check() - this.analyze() - let res = (new AttrConditionPlanner()) - .withTerms(this.termIdx) - .withConditions(this.cond) - .withAggregatedAttr(this.aggregatedAttr) - .withMain((new InitIndexPlanner()).build()) - if (this.preCond) { - const preCond = (new AttrConditionPlanner()) - .withTerms(this.precondTermsIdx) - .withConditions(this.preCond) - .withMain((new InitIndexPlanner()).build()) - res = res.withPrecondition(preCond.build()) - } - res = res.build() - res = (new IndexGroupByPlanner()).withMain(res).build() - if (this.aggFn) { - res = (new AggregatorPlanner()) - .withFn(this.aggFn) - .withAttr(this.aggregatedAttr) - .withCompareFn(this.script.Child('cmp').value) - .withCompareVal(this.script.Child('cmp_val').value) - .withMain(res) - .build() - } - res = (new IndexLimitPlanner()).withMain(res).build() - res = (new TracesDataPlanner()).withMain(res).build() - res = (new IndexLimitPlanner()).withMain(res).build() - - return res - } - - planEval () { - this.check() - this.analyze() - const res = (new AttrConditionEvalPlanner()) - .withTerms(this.termIdx) - .withConditions(this.cond) - .withAggregatedAttr(this.aggregatedAttr) - .withMain((new InitIndexPlanner()).build()) - .build() - - return res - } - - setEvaluationResult (result) { - this.eval = {} - for (const row of result) { - this.eval[row.cond] = row.count - } - } - - minify () { - const subcost = {} - for (let i = 0; i < this.termIdx.length; i++) { - subcost[this.termIdx[i].value] = Object.entries(this.eval) - .find(x => parseInt(x[0]) === i + 1) - subcost[this.termIdx[i].value] = subcost[this.termIdx[i].value] - ? parseInt(subcost[this.termIdx[i].value][1]) - : 0 - } - if (!this.isDNF(this.cond)) { - return this.estimateCost(this.cond, subcost) - } - this.preCond = this.getSimplePrecondition(this.cond, subcost) - if (this.preCond) { - this.extractTermsIdx(this.preCond, this.precondTermsIdx, this.preCondTerms) - } - - return this.preCond - ? this.estimateCost(this.preCond, subcost) - : this.estimateCost(this.cond, subcost) - } - - check () { - if (this.script.Children('SYNTAX').length > 1) { - throw new Error('more than one selector is not supported') - } - } - - analyze () { - this.terms = {} - this.cond = this.analyzeCond(this.script.Child('attr_selector_exp')) - this.analyzeAgg() - } - - /** - * - * @param token {Token} - * @param tree {{root: any}} - * @param place {{ref: any}} - */ - buildExpressionTree (token, tree, place) { - if (token.name !== 'attr_selector_exp') { - throw new Error('unsupported selector') - } - let leftHand = token.tokens[0] - if (token.tokens[0].name === 'complex_head') { - const newTree = { root: { ref: null } } - this.buildExpressionTree(token.tokens[0].tokens.find(x => x.name === 'attr_selector_exp'), - newTree, - newTree.root - ) - leftHand = newTree.root - } - const tail = token.tokens.find(x => x.name === 'tail') - if (!tail) { - // if we have `a` - place.ref = leftHand - return - } - const andOr = token.tokens.find(x => x.name === 'and_or').value - const newPlace = { ref: null } - switch (andOr) { - case '&&': - place.ref = ['&&', { ref: leftHand }, newPlace] - this.buildExpressionTree(tail.tokens[0], tree, newPlace) - return - case '||': - place.ref = leftHand - tree.root = ['||', { ref: tree.root }, newPlace] - this.buildExpressionTree(tail.tokens[0], tree, newPlace) - } - } - - /** - * - * @param t {{ref: any} | Token | Array} - * @returns {Token | Array} - */ - minimizeTree (t) { - while (t.ref) { - t = t.ref - } - if (!Array.isArray(t)) { - return t - } - for (let i = t.length - 1; i > 0; i--) { - t[i] = this.minimizeTree(t[i]) - if (Array.isArray(t[i]) && t[i][0] === t[0]) { - t.splice(i, 1, ...t[i].slice(1)) - } - } - return t - } - - /** - * @param t {Token | Array} - * @returns {boolean} - */ - isDNF (t) { - if (t.name) { - return true - } - const fn = t[0] - for (let i = 1; i < t.length; i++) { - if (!this.isDNF(t[i])) { - return false - } - if (Array.isArray(t[i]) && fn === '&&' && t[i][0] === '||') { - return false - } - } - return true - } - - /** - * - * @param t {Token | Array} - * @param subcosts {{[key: string]: number}} - * @returns number - */ - estimateCost (t, subcosts) { - if (t.name) { - return subcosts[t.value] - } - const fn = t[0] - const costs = t.slice(1).map(x => this.estimateCost(x, subcosts)) - switch (fn) { - case '&&': - return Math.min(...costs) - case '||': - return costs.reduce((a, b) => a + b) - } - throw new Error('unsupported function') - } - - /** - * - * @param t {Token | Array} - * @param subcosts {{[key: string]: number}} - */ - getSimplePrecondition (t, subcosts) { - if (!this.isDNF(t)) { - return null - } - if (t.name) { - return subcosts[t.value] < 10000000 ? t : null - } - const fn = t[0] - const self = this - const simplify = x => x.length === 2 ? x[1] : x - if (fn === '&&') { - const res = t.slice(1).filter(x => self.estimateCost(x, subcosts) < 10000000) - return res.length > 0 ? simplify(['&&', ...res]) : null - } - if (fn === '||') { - const res = t.slice(1).map(x => self.getSimplePrecondition(x, subcosts)).filter(x => x) - return res.length === t.length - 1 ? simplify(['||', ...res]) : null - } - throw new Error('unsupported function') - } - - /** - * - * @param token {Token} - */ - analyzeCond (token) { - const tree = { root: { ref: null } } - this.buildExpressionTree(token, tree, tree.root) - tree.root = this.minimizeTree(tree.root) - this.extractTermsIdx(tree.root, this.termIdx, this.terms) - return tree.root - } - - extractTermsIdx (t, termIdx, terms) { - const self = this - if (t.name) { - if (!terms[t.value]) { - termIdx.push(t) - terms[t.value] = termIdx.length - t.termIdx = termIdx.length - 1 - } else { - t.termIdx = terms[t.value] - 1 - } - return - } - if (Array.isArray(t)) { - t.forEach(x => self.extractTermsIdx(x, termIdx, terms)) - } - } - - analyzeAgg () { - const agg = this.script.Child('aggregator') - if (!agg) { - return - } - if (['count', 'sum', 'min', 'max', 'avg'].indexOf(agg.Child('fn').value) < 0) { - return - } - this.aggFn = agg.Child('fn').value - const labelName = agg.Child('attr').Child('label_name') - this.aggregatedAttr = labelName ? labelName.value : '' - this.cmpVal = agg.Child('cmp_val').value - } -} diff --git a/traceql/clickhouse_transpiler/init.js b/traceql/clickhouse_transpiler/init.js deleted file mode 100644 index 1deec485..00000000 --- a/traceql/clickhouse_transpiler/init.js +++ /dev/null @@ -1,38 +0,0 @@ -const Sql = require('@cloki/clickhouse-sql') -const { format } = require('date-fns') -const { standardBuilder } = require('./shared') -/** - * @typedef {{ - * from: Date, - * to: Date, - * tracesAttrsTable: string, - * limit: number, - * isCluster: boolean, - * tracesTable: string, - * tracesDistTable: string, - * randomFilter: number[]|undefined, - * cachedTraceIds: string[]|undefined, - * planner: Planner - * }} Context - */ -/** - * @typedef {function(Sql.Select, Context): Select} ProcessFn - */ - -/** - * @type {ProcessFn} - */ -module.exports = standardBuilder((sel, ctx) => { - return (new Sql.Select()).select(['trace_id', 'trace_id'], - [new Sql.Raw('span_id'), 'span_id'], - [new Sql.Raw('any(duration)'), 'duration'], - [new Sql.Raw('any(timestamp_ns)'), 'timestamp_ns']) - .from([ctx.tracesAttrsTable, 'traces_idx']) - .where(Sql.And( - Sql.Gte('date', Sql.val(format(ctx.from, 'yyyy-MM-dd'))), - Sql.Lte('date', Sql.val(format(ctx.to, 'yyyy-MM-dd'))), - Sql.Gte('traces_idx.timestamp_ns', new Sql.Raw(ctx.from.getTime() + '000000')), - Sql.Lt('traces_idx.timestamp_ns', new Sql.Raw(ctx.to.getTime() + '000000')) - )).groupBy('trace_id', 'span_id') - .orderBy(['timestamp_ns', 'desc']) -}) diff --git a/traceql/clickhouse_transpiler/limit.js b/traceql/clickhouse_transpiler/limit.js deleted file mode 100644 index 3ec1c224..00000000 --- a/traceql/clickhouse_transpiler/limit.js +++ /dev/null @@ -1,8 +0,0 @@ -const { standardBuilder } = require('./shared') - -module.exports = standardBuilder((sel, ctx) => { - if (!ctx.limit) { - return sel - } - return sel.limit(ctx.limit) -}) diff --git a/traceql/clickhouse_transpiler/shared.js b/traceql/clickhouse_transpiler/shared.js deleted file mode 100644 index 8869d9d6..00000000 --- a/traceql/clickhouse_transpiler/shared.js +++ /dev/null @@ -1,98 +0,0 @@ -const Sql = require('@cloki/clickhouse-sql') -/** - * - * @param op {string} - */ -module.exports.getCompareFn = (op) => { - switch (op) { - case '=': - return Sql.Eq - case '>': - return Sql.Gt - case '<': - return Sql.Lt - case '>=': - return Sql.Gte - case '<=': - return Sql.Lte - case '!=': - return Sql.Ne - } - throw new Error('not supported operator: ' + op) -} - -module.exports.durationToNs = (duration) => { - const measurements = { - ns: 1, - us: 1000, - ms: 1000000, - s: 1000000000, - m: 1000000000 * 60, - h: 1000000000 * 3600, - d: 1000000000 * 3600 * 24 - } - const durationRe = duration.match(/(\d+\.?\d*)(ns|us|ms|s|m|h|d)?/) - if (!durationRe) { - throw new Error('Invalid duration compare value') - } - return parseFloat(durationRe[1]) * measurements[durationRe[2].toLowerCase()] -} - -module.exports.unquote = (val) => { - if (val[0] === '"') { - return JSON.parse(val) - } - if (val[0] === '`') { - return val.substr(1, val.length - 2) - } - throw new Error('unquote not supported') -} - -/** - * @typedef {function(Context): Select} BuiltProcessFn - */ -/** - * @param fn {ProcessFn} - * @returns {{ - * new(): { - * withMain(BuiltProcessFn): this, - * build(): BuiltProcessFn - * }, - * prototype: { - * withMain(BuiltProcessFn): this, - * build(): BuiltProcessFn - * }}} - */ -module.exports.standardBuilder = (fn) => { - return class { - withMain (main) { - this.main = main - return this - } - - /** - * - * @returns {BuiltProcessFn} - */ - build () { - return (ctx) => { - const sel = this.main ? this.main(ctx) : null - return fn(sel, ctx) - } - } - } -} - -/** - * - * @param terms {SQLObject[]} - * @returns {SQLObject} - */ -module.exports.bitSet = (terms) => { - const res = new Sql.Raw('') - res.terms = terms - res.toString = () => { - return res.terms.map((t, i) => `bitShiftLeft(toUInt64(${t.toString()}), ${i})`).join('+') - } - return res -} diff --git a/traceql/clickhouse_transpiler/traces_data.js b/traceql/clickhouse_transpiler/traces_data.js deleted file mode 100644 index 6023e41d..00000000 --- a/traceql/clickhouse_transpiler/traces_data.js +++ /dev/null @@ -1,37 +0,0 @@ -const Sql = require('@cloki/clickhouse-sql') -const { standardBuilder } = require('./shared') -/** - * @type {ProcessFn} - */ -const processFn = (sel, ctx) => { - const _in = ctx.isCluster ? 'global in' : 'in' - const table = !ctx.isCluster ? ctx.tracesTable : ctx.tracesDistTable - const withMain = new Sql.With('index_grouped', sel) - const withTraceIds = new Sql.With('trace_ids', (new Sql.Select()) - .select('trace_id') - .from(new Sql.WithReference(withMain))) - const withTraceIdsSpanIds = new Sql.With('trace_span_ids', (new Sql.Select()) - .select('trace_id', 'span_id') - .from(new Sql.WithReference(withMain)) - .join('span_id', 'array')) - return (new Sql.Select()) - .with(withMain, withTraceIds, withTraceIdsSpanIds) - .select( - [new Sql.Raw('lower(hex(traces.trace_id))'), 'trace_id'], - [new Sql.Raw(`arrayMap(x -> lower(hex(x)), groupArrayIf(traces.span_id, (traces.trace_id, traces.span_id) ${_in} ${new Sql.WithReference(withTraceIdsSpanIds)}))`), 'span_id'], - [new Sql.Raw(`groupArrayIf(traces.duration_ns, (traces.trace_id, traces.span_id) ${_in} ${new Sql.WithReference(withTraceIdsSpanIds)})`), 'duration'], - [new Sql.Raw(`groupArrayIf(traces.timestamp_ns, (traces.trace_id, traces.span_id) ${_in} ${new Sql.WithReference(withTraceIdsSpanIds)})`), 'timestamp_ns'], - [new Sql.Raw('min(traces.timestamp_ns)'), 'start_time_unix_nano'], - [new Sql.Raw( - 'toFloat64(max(traces.timestamp_ns + traces.duration_ns) - min(traces.timestamp_ns)) / 1000000' - ), 'duration_ms'], - [new Sql.Raw('argMin(traces.name, traces.timestamp_ns)', 'root_service_name'), 'root_service_name'], - [new Sql.Raw(`groupArrayIf(base64Encode(traces.payload), (traces.trace_id, traces.span_id) ${_in} ${new Sql.WithReference(withTraceIdsSpanIds)})`), 'payload'], - [new Sql.Raw(`groupArrayIf(traces.payload_type, (traces.trace_id, traces.span_id) ${_in} ${new Sql.WithReference(withTraceIdsSpanIds)})`), 'payload_type'] - ).from([table, 'traces']).where(Sql.And( - new Sql.In(new Sql.Raw('traces.trace_id'), _in, new Sql.WithReference(withTraceIds)) - )).groupBy('traces.trace_id') - .orderBy(['start_time_unix_nano', 'desc']) -} - -module.exports = standardBuilder(processFn) diff --git a/traceql/index.js b/traceql/index.js deleted file mode 100644 index 5b0bf729..00000000 --- a/traceql/index.js +++ /dev/null @@ -1,158 +0,0 @@ -const parser = require('./parser') -const { Planner } = require('./clickhouse_transpiler') -const logger = require('../lib/logger') -const { DATABASE_NAME } = require('../lib/utils') -const { clusterName } = require('../common') -const { rawRequest } = require('../lib/db/clickhouse') -const { postProcess } = require('./post_processor') - -/** - * - * @param query {string} - * @param limit {number} - * @param from {Date} - * @param to {Date} - * @returns {Promise<[]>} - */ -const search = async (query, limit, from, to) => { - const _dbname = DATABASE_NAME() - const scrpit = parser.ParseScript(query) - /** @type {Context} */ - const ctx = { - tracesDistTable: `${_dbname}.tempo_traces_dist`, - tracesTable: `${_dbname}.tempo_traces`, - isCluster: !!clusterName, - tracesAttrsTable: `${_dbname}.tempo_traces_attrs_gin`, - from: from, - to: to, - limit: limit, - randomFilter: null, - planner: new Planner(scrpit.rootToken) - } - - let complexity = await evaluateComplexity(ctx, scrpit.rootToken) - let res = [] - if (complexity > 10000000) { - complexity = ctx.planner.minify() - } - if (complexity > 10000000) { - res = await processComplexResult(ctx, scrpit.rootToken, complexity) - } else { - res = await processSmallResult(ctx, scrpit.rootToken) - } - res = postProcess(res, scrpit.rootToken) - res.forEach(t => - t.spanSets.forEach( - ss => ss.spans.sort( - (a, b) => b.startTimeUnixNano.localeCompare(a.startTimeUnixNano)) - ) - ) - return res -} - -/** - * - * @param ctx {Context} - * @param script {Token} - */ -const evaluateComplexity = async (ctx, script) => { - const evaluator = ctx.planner.planEval() - const sql = evaluator(ctx) - const response = await rawRequest(sql + ' FORMAT JSON', null, DATABASE_NAME()) - ctx.planner.setEvaluationResult(response.data.data) - return response.data.data.reduce((acc, row) => Math.max(acc, row.count), 0) -} - -/** - * - * @param ctx {Context} - * @param script {Token} - * @param complexity {number} - */ -async function processComplexResult (ctx, script, complexity) { - const planner = ctx.planner.plan() - const maxFilter = Math.floor(complexity / 10000000) - //let traces = [] - let response = null - for (let i = 0; i < maxFilter; i++) { - ctx.randomFilter = [maxFilter, i] - const sql = planner(ctx) - response = await rawRequest(sql + ' FORMAT JSON', null, DATABASE_NAME()) - if (response.data.data.length === parseInt(ctx.limit)) { - const minStart = response.data.data.reduce((acc, row) => - acc === 0 ? row.start_time_unix_nano : Math.min(acc, row.start_time_unix_nano), 0 - ) - ctx.from = new Date(Math.floor(minStart / 1000000)) - ctx.randomFilter = null - complexity = await evaluateComplexity(ctx, script) - if (complexity <= 10000000) { - return await processSmallResult(ctx, script) - } - ctx.randomFilter = [maxFilter, i] - } - ctx.cachedTraceIds = response.data.data.map(row => row.trace_id) - /*traces = response.data.data.map(row => ({ - traceID: row.trace_id, - rootServiceName: row.root_service_name, - rootTraceName: row.root_trace_name, - startTimeUnixNano: row.start_time_unix_nano, - durationMs: row.duration_ms, - spanSets: [ - { - spans: row.span_id.map((spanId, i) => ({ - spanID: spanId, - startTimeUnixNano: row.timestamp_ns[i], - durationNanos: row.duration[i], - attributes: [] - })), - matched: row.span_id.length - } - ] - }))*/ - } - return response.data.data -} - -/** - * - * @param ctx {Context} - * @param script {Token} - */ -async function processSmallResult (ctx, script) { - const planner = ctx.planner.plan() - const sql = planner(ctx) - const response = await rawRequest(sql + ' FORMAT JSON', null, DATABASE_NAME()) - /*const traces = response.data.data.map(row => ({ - traceID: row.trace_id, - rootServiceName: row.root_service_name, - rootTraceName: row.root_trace_name, - startTimeUnixNano: row.start_time_unix_nano, - durationMs: row.duration_ms, - spanSet: { - spans: row.span_id.map((spanId, i) => ({ - spanID: spanId, - startTimeUnixNano: row.timestamp_ns[i], - spanStartTime: row.timestamp_ns[i], - durationNanos: row.duration[i], - attributes: [] - })), - matched: row.span_id.length - }, - spanSets: [ - { - spans: row.span_id.map((spanId, i) => ({ - spanID: spanId, - startTimeUnixNano: row.timestamp_ns[i], - durationNanos: row.duration[i], - attributes: [] - })), - matched: row.span_id.length - } - ] - }))*/ - return response.data.data -} - -module.exports = { - search -} diff --git a/traceql/parser.js b/traceql/parser.js deleted file mode 100644 index 42b48730..00000000 --- a/traceql/parser.js +++ /dev/null @@ -1,9 +0,0 @@ -const fs = require('fs') -const path = require('path') -const { Compiler } = require('bnf') - -const bnf = fs.readFileSync(path.join(__dirname, 'traceql.bnf')).toString() -const compiler = new Compiler() -compiler.AddLanguage(bnf, 'traceql') - -module.exports = compiler diff --git a/traceql/post_processor/index.js b/traceql/post_processor/index.js deleted file mode 100644 index 124711d2..00000000 --- a/traceql/post_processor/index.js +++ /dev/null @@ -1,77 +0,0 @@ -const Zipkin = require('../../lib/db/zipkin') -const { flatOTLPAttrs, OTLPgetServiceNames } = require('../../lib/utils') -/** - * - * @param rows {Row[]} - * @param script {Token} - */ -function postProcess (rows, script) { - const selectAttrs = script.Children('aggregator') - .filter(x => x.Child('fn').value === 'select') - .map(x => x.Children('label_name')) - .reduce((acc, x) => { - let attrs = x.map(y => ({ - name: y.value, - path: y.value.split('.').filter(y => y) - })) - if (attrs[0] === 'span' || attrs[0] === 'resource') { - attrs = attrs.slice(1) - } - return [...acc, ...attrs] - }, []) - rows = rows.map(row => ({ - ...row, - objs: row.payload.map((payload, i) => { - let span = null - let attrs = null - let serviceName = null - - switch (row.payload_type[i]) { - case 1: - return new Zipkin(JSON.parse(Buffer.from(payload, 'base64').toString())) - case 2: - span = JSON.parse(Buffer.from(payload, 'base64').toString()) - attrs = flatOTLPAttrs(span.attributes) - serviceName = OTLPgetServiceNames(attrs) - attrs.name = span.name - attrs['service.name'] = serviceName.local - if (serviceName.remote) { - attrs['remoteService.name'] = serviceName.remote - } - attrs = [...Object.entries(attrs)] - return { tags: attrs } - } - return null - }) - })) - const spans = (row) => row.span_id.map((spanId, i) => ({ - spanID: spanId, - startTimeUnixNano: row.timestamp_ns[i], - durationNanos: row.duration[i], - attributes: selectAttrs.map(attr => ({ - key: attr.name, - value: { - stringValue: (row.objs[i].tags.find(t => t[0] === attr.path.join('.')) || [null, null])[1] - } - })).filter(x => x.value.stringValue) - })) - const traces = rows.map(row => ({ - traceID: row.trace_id, - rootServiceName: row.root_service_name, - rootTraceName: row.root_trace_name, - startTimeUnixNano: row.start_time_unix_nano, - durationMs: row.duration_ms, - spanSet: { spans: spans(row) }, - spanSets: [ - { - spans: spans(row), - matched: row.span_id.length - } - ] - })) - return traces -} - -module.exports = { - postProcess -} diff --git a/traceql/post_processor/types.d.ts b/traceql/post_processor/types.d.ts deleted file mode 100644 index 5a92e85b..00000000 --- a/traceql/post_processor/types.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface Row { - trace_id: string; - span_id: string[]; - duration: string[]; - timestamp_ns: string[]; - start_time_unix_nano: string; - duration_ms: number; - root_service_name: string; - payload: string[]; - payload_type: number[]; -} \ No newline at end of file diff --git a/traceql/traceql.bnf b/traceql/traceql.bnf deleted file mode 100644 index 28e447b1..00000000 --- a/traceql/traceql.bnf +++ /dev/null @@ -1,24 +0,0 @@ -<SYNTAX> ::= <selector> *(<OWSP> <and_or> <OWSP> <SYNTAX>) - -selector ::= "{" <OWSP> <attr_selector_exp> <OWSP> "}" [<OWSP> <aggregator>] -attr_selector_exp ::= (<attr_selector> | <complex_head> ) [ <OWSP> <and_or> <OWSP> <tail> ] -complex_head ::= "(" <OWSP> <attr_selector_exp> <OWSP> ")" -tail ::= <attr_selector_exp> -and_or ::= "&&" | "||" - -aggregator ::= "|" <OWSP> <fn> <OWSP> <attr> <OWSP> [ <cmp> <OWSP> <cmp_val> ] -fn ::= "count"|"sum"|"min"|"max"|"avg"|"select" -attr ::= "(" <OWSP> [ <label_names> ] <OWSP> ")" -cmp ::= "="|"!="|"<="|">="|"<"|">" -cmp_val ::= <number> [<measurement>] -measurement ::= "ns"|"us"|"ms"|"s"|"m"|"h"|"d" - -label_name ::= ("." | <ALPHA> | "-" | "_") *("." | <ALPHA> | "_" | "-" | <DIGITS>) -label_names ::= <label_name> *(<OWSP> "," <OWSP> <label_name>) -number ::= ["-"] <DIGITS> ["." <DIGITS>] - -attr_selector ::= <label_name> <OWSP> <op> <OWSP> <value> -op ::= ">="|"<="|"=~"|"="|"!="|"<"|">"|"!~" -value ::= <time_val> | <number> | <quoted_str> -time_val ::= (<DIGITS> ["." <DIGITS>]) <measurement> -quoted_str ::= (<QUOTE><QUOTE>) | (<AQUOTE><AQUOTE>) | <QLITERAL> | <AQLITERAL> diff --git a/view/main.go b/view/main.go new file mode 100644 index 00000000..d9b92df2 --- /dev/null +++ b/view/main.go @@ -0,0 +1,50 @@ +package view + +import ( + "github.com/gorilla/mux" + clconfig "github.com/metrico/cloki-config" + "io/fs" + "net/http" + "strings" +) + +var config *clconfig.ClokiConfig + +func Init(cfg *clconfig.ClokiConfig, mux *mux.Router) { + if !HaveStatic { + return + } + + config = cfg + + staticSub, err := fs.Sub(Static, "dist") + if err != nil { + panic(err) + } + fileServer := http.FileServer(http.FS(staticSub)) + + prefix := "/" + if config.Setting.ClokiReader.ViewPath != "/etc/qryn-view" { + prefix = config.Setting.ClokiReader.ViewPath + } + + // Serve static files + viewPath := strings.TrimSuffix(config.Setting.ClokiReader.ViewPath, "/") + for _, path := range []string{ + viewPath + "/", + viewPath + "/plugins", + viewPath + "/users", + viewPath + "/datasources", + viewPath + "/datasources/{ds}"} { + mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { + contents, err := Static.ReadFile("/dist/index.html") + if err != nil { + w.WriteHeader(404) + return + } + w.Header().Set("Content-Type", "text/html") + w.Write(contents) + }) + } + mux.PathPrefix(prefix).Handler(fileServer) +} diff --git a/view/static.go b/view/static.go new file mode 100644 index 00000000..0e66be97 --- /dev/null +++ b/view/static.go @@ -0,0 +1,11 @@ +//go:build view +// +build view + +package view + +import "embed" + +//go:embed dist +var Static embed.FS + +var HaveStatic bool = true diff --git a/view/static_no.go b/view/static_no.go new file mode 100644 index 00000000..fc48d368 --- /dev/null +++ b/view/static_no.go @@ -0,0 +1,10 @@ +//go:build !view +// +build !view + +package view + +import "embed" + +var Static embed.FS + +var HaveStatic bool = false diff --git a/wasm_parts/go.mod b/wasm_parts/go.mod deleted file mode 100644 index 87605990..00000000 --- a/wasm_parts/go.mod +++ /dev/null @@ -1,63 +0,0 @@ -module wasm_parts - -go 1.21 - -toolchain go1.21.3 - -replace ( - cloud.google.com/go v0.65.0 => cloud.google.com/go v0.102.1 - github.com/InfluxCommunity/influxdb3-go v0.2.0 => github.com/akvlad/influxdb3-go v0.0.1 - github.com/docker/distribution v2.7.1+incompatible => github.com/docker/distribution v2.8.0+incompatible - github.com/json-iterator/go v1.1.12 => ./json.iterator - k8s.io/client-go v12.0.0+incompatible => k8s.io/client-go v0.22.1 -) - -require ( - github.com/alecthomas/participle/v2 v2.1.0 - github.com/metrico/micro-gc v0.0.4 - github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 - github.com/prometheus/prometheus v1.8.2-0.20220714142409-b41e0750abf5 -) - -require ( - github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect - github.com/aws/aws-sdk-go v1.44.45 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dennwc/varint v1.0.0 // indirect - github.com/edsrzf/mmap-go v1.1.0 // indirect - github.com/go-kit/log v0.2.1 // indirect - github.com/go-logfmt/logfmt v0.5.1 // indirect - github.com/go-logr/logr v1.2.3 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/grafana/regexp v0.0.0-20220304095617-2e8d9baf4ac2 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/jpillora/backoff v1.0.0 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect - github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect - github.com/oklog/ulid v1.3.1 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.12.2 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/common/sigv4 v0.1.0 // indirect - github.com/prometheus/procfs v0.7.3 // indirect - github.com/stretchr/testify v1.8.0 // indirect - go.opentelemetry.io/otel v1.7.0 // indirect - go.opentelemetry.io/otel/trace v1.7.0 // indirect - go.uber.org/atomic v1.9.0 // indirect - go.uber.org/goleak v1.1.12 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/oauth2 v0.0.0-20220628200809-02e64fa58f26 // indirect - golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.33.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/wasm_parts/go.sum b/wasm_parts/go.sum deleted file mode 100644 index b7cff960..00000000 --- a/wasm_parts/go.sum +++ /dev/null @@ -1,952 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9Uk= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw= -github.com/Azure/azure-sdk-for-go v65.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= -github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= -github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg= -github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= -github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/alecthomas/assert/v2 v2.3.0 h1:mAsH2wmvjsuvyBvAmCtm7zFsBlb8mIHx5ySLVdDZXL0= -github.com/alecthomas/assert/v2 v2.3.0/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= -github.com/alecthomas/participle/v2 v2.1.0 h1:z7dElHRrOEEq45F2TG5cbQihMtNTv8vwldytDj7Wrz4= -github.com/alecthomas/participle/v2 v2.1.0/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= -github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= -github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/armon/go-metrics v0.3.3 h1:a9F4rlj7EWWrbj7BYw8J8+x+ZZkJeqzNyRk8hdPF+ro= -github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.44.45 h1:E2i73X4QdVS0XrfX/aVPt/M0Su2IuJ7AFvAMtF0id1Q= -github.com/aws/aws-sdk-go v1.44.45/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc h1:PYXxkRUBGUMa5xgMVMDl62vEklZvKpVaxQeN9ie7Hfk= -github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE= -github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= -github.com/digitalocean/godo v1.81.0 h1:sjb3fOfPfSlUQUK22E87BcI8Zx2qtnF7VUCCO4UK3C8= -github.com/digitalocean/godo v1.81.0/go.mod h1:BPCqvwbjbGqxuUnIKB4EvS/AX7IDnNmt5fwvIkWo+ew= -github.com/docker/distribution v2.8.0+incompatible h1:l9EaZDICImO1ngI+uTifW+ZYvvz7fKISBAKpg+MbWbY= -github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE= -github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= -github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= -github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/go-control-plane v0.10.3 h1:xdCVXxEe0Y3FQith+0cj2irwZudqGYvecuLB1HtdexY= -github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.7 h1:qcZcULcd/abmQg6dwigimCNEyi4gg31M/xaciQlDml8= -github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= -github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= -github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 h1:JVrqSeQfdhYRFk24TvhTZWU0q8lfCojxZQFi3Ou7+uY= -github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-zookeeper/zk v1.0.2 h1:4mx0EYENAdX/B/rbunjlt5+4RTA/a9SMHBRuSKdGxPM= -github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/gophercloud/gophercloud v0.25.0 h1:C3Oae7y0fUVQGSsBrb3zliAjdX+riCSEh4lNMejFNI4= -github.com/gophercloud/gophercloud v0.25.0/go.mod h1:Q8fZtyi5zZxPS/j9aj3sSxtvj41AdQMDwyo1myduD5c= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grafana/regexp v0.0.0-20220304095617-2e8d9baf4ac2 h1:uirlL/j72L93RhV4+mkWhjv0cov2I0MIgPOG9rMDr1k= -github.com/grafana/regexp v0.0.0-20220304095617-2e8d9baf4ac2/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.13.0 h1:2hnLQ0GjQvw7f3O61jMO8gbasZviZTrt9R8WzgiirHc= -github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= -github.com/hashicorp/cronexpr v1.1.1 h1:NJZDd87hGXjoZBdvyCF9mX4DCq5Wy7+A/w+A7q0wn6c= -github.com/hashicorp/cronexpr v1.1.1/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= -github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.12.2 h1:F1fdYblUEsxKiailtkhCCG2g4bipEgaHiDc8vffNpD4= -github.com/hashicorp/go-hclog v0.12.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-immutable-radix v1.2.0 h1:l6UW37iCXwZkZoAbEYnptSHVE/cQ5bOTPYG5W3vf9+8= -github.com/hashicorp/go-immutable-radix v1.2.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= -github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/nomad/api v0.0.0-20220629141207-c2428e1673ec h1:jAF71e0KoaY2LJlRsRxxGz6MNQOG5gTBIc+rklxfNO0= -github.com/hashicorp/nomad/api v0.0.0-20220629141207-c2428e1673ec/go.mod h1:jP79oXjopTyH6E8LF0CEMq67STgrlmBRIyijA0tuR5o= -github.com/hashicorp/serf v0.9.6 h1:uuEX1kLR6aoda1TBttmJQKDLZE1Ob7KN0NPdE7EtCDc= -github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= -github.com/hetznercloud/hcloud-go v1.35.0 h1:sduXOrWM0/sJXwBty7EQd7+RXEJh5+CsAGQmHshChFg= -github.com/hetznercloud/hcloud-go v1.35.0/go.mod h1:mepQwR6va27S3UQthaEPGS86jtzSY9xWL1e9dyxXpgA= -github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= -github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/ionos-cloud/sdk-go/v6 v6.1.0 h1:0EZz5H+t6W23zHt6dgHYkKavr72/30O9nA97E3FZaS4= -github.com/ionos-cloud/sdk-go/v6 v6.1.0/go.mod h1:Ox3W0iiEz0GHnfY9e5LmAxwklsxguuNFEUSu0gVRTME= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b h1:iNjcivnc6lhbvJA3LD622NPrUponluJrBWPIwGG/3Bg= -github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/linode/linodego v1.8.0 h1:7B2UaWu6C48tZZZrtINWRElAcwzk4TLnL9USjKf3xm0= -github.com/linode/linodego v1.8.0/go.mod h1:heqhl91D8QTPVm2k9qZHP78zzbOdTFLXE9NJc3bcc50= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/metrico/micro-gc v0.0.4 h1:1A1ewEnH4obARpcxVFAk7hvtvnfGK1RPvrWCDSOZYM4= -github.com/metrico/micro-gc v0.0.4/go.mod h1:spP30j8DlbA8Rp6gHf6oMFA1ltEX7hX8EHhZqBp9fSI= -github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 h1:xoIK0ctDddBMnc74udxJYBqlo9Ylnsp1waqjLsnef20= -github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= -github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/prometheus v1.8.2-0.20220714142409-b41e0750abf5 h1:y/PIlkj30dGwBdI2HHbYx3Z6eFRmbpznt/Bwp/Ca6qg= -github.com/prometheus/prometheus v1.8.2-0.20220714142409-b41e0750abf5/go.mod h1:egARUgz+K93zwqsVIAneFlLZefyGOON44WyAp4Xqbbk= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9 h1:0roa6gXKgyta64uqh52AQG3wzZXH21unn+ltzQSXML0= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= -github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM= -go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= -go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o= -go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220628200809-02e64fa58f26 h1:uBgVQYJLi/m8M0wzp+aGwBWt90gMRoOVf+aWTW10QHI= -golang.org/x/oauth2 v0.0.0-20220628200809-02e64fa58f26/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U= -golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03 h1:W70HjnmXFJm+8RNjOpIDYW2nKsSi/af0VvIZUtYkwuU= -google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= -gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.24.2 h1:g518dPU/L7VRLxWfcadQn2OnsiGWVOadTLpdnqgY2OI= -k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg= -k8s.io/apimachinery v0.24.2 h1:5QlH9SL2C8KMcrNJPor+LbXVTaZRReml7svPEh4OKDM= -k8s.io/apimachinery v0.24.2/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/client-go v0.24.2 h1:CoXFSf8if+bLEbinDqN9ePIDGzcLtqhfd6jpfnwGOFA= -k8s.io/client-go v0.24.2/go.mod h1:zg4Xaoo+umDsfCWr4fCnmLEtQXyCNXCvJuSsglNcV30= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= -k8s.io/klog/v2 v2.70.0 h1:GMmmjoFOrNepPN0ZeGCzvD2Gh5IKRwdFx8W5PBxVTQU= -k8s.io/klog/v2 v2.70.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 h1:Gii5eqf+GmIEwGNKQYQClCayuJCe2/4fZUvF7VG99sU= -k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= -k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= -k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y= -sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/wasm_parts/json.iterator/fake.go b/wasm_parts/json.iterator/fake.go deleted file mode 100644 index a02e048c..00000000 --- a/wasm_parts/json.iterator/fake.go +++ /dev/null @@ -1,13 +0,0 @@ -package json_iterator - -func RegisterTypeEncoderFunc(name string, f1 any, f2 any) { -} - -type API struct { -} - -func (api API) Marshal(v any) ([]byte, error) { - return nil, nil -} - -var ConfigCompatibleWithStandardLibrary = API{} diff --git a/wasm_parts/main.go b/wasm_parts/main.go deleted file mode 100644 index 7f3ad847..00000000 --- a/wasm_parts/main.go +++ /dev/null @@ -1,808 +0,0 @@ -package main - -import ( - "context" - "fmt" - gcContext "github.com/metrico/micro-gc/context" - "github.com/prometheus/prometheus/model/labels" - "github.com/prometheus/prometheus/promql" - "github.com/prometheus/prometheus/promql/parser" - "github.com/prometheus/prometheus/storage" - "github.com/prometheus/prometheus/tsdb/chunkenc" - "strconv" - "strings" - "time" - "unsafe" - promql2 "wasm_parts/promql" - shared2 "wasm_parts/promql/shared" - sql "wasm_parts/sql_select" - parser2 "wasm_parts/traceql/parser" - traceql_transpiler "wasm_parts/traceql/transpiler" - "wasm_parts/types" -) - -var maxSamples = 5000000 - -type ctx struct { - onDataLoad func(c *ctx) - request []byte - response []byte -} - -var data = map[uint32]*ctx{} - -//export createCtx -func createCtx(id uint32) { - ctxId := gcContext.GetContextID() - gcContext.SetContext(id) - c := &ctx{} - gcContext.SetContext(ctxId) - data[id] = c -} - -//export alloc -func alloc(id uint32, size int) *byte { - ctxId := gcContext.GetContextID() - gcContext.SetContext(id) - data[id].request = make([]byte, size) - gcContext.SetContext(ctxId) - return &data[id].request[0] -} - -//export dealloc -func dealloc(id uint32) { - delete(data, id) - gcContext.ReleaseContext(id) -} - -//export getCtxRequest -func getCtxRequest(id uint32) *byte { - return &data[id].request[0] -} - -//export getCtxRequestLen -func getCtxRequestLen(id uint32) uint32 { - return uint32(len(data[id].request)) -} - -//export getCtxResponse -func getCtxResponse(id uint32) *byte { - return &data[id].response[0] -} - -//export getCtxResponseLen -func getCtxResponseLen(id uint32) uint32 { - return uint32(len(data[id].response)) -} - -//export transpileTraceQL -func transpileTraceQL(id uint32) int { - ctxId := gcContext.GetContextID() - gcContext.SetContext(id) - defer gcContext.SetContext(ctxId) - - request := types.TraceQLRequest{} - err := request.UnmarshalJSON(data[id].request) - if err != nil { - data[id].response = []byte(err.Error()) - return 1 - } - - script, err := parser2.Parse(request.Request) - if err != nil { - data[id].response = []byte(err.Error()) - return 1 - } - - planner, err := traceql_transpiler.Plan(script) - if err != nil { - data[id].response = []byte(err.Error()) - return 1 - } - request.Ctx.Ctx = context.Background() - request.Ctx.CancelCtx = func() {} - request.Ctx.CHSqlCtx = &sql.Ctx{ - Params: make(map[string]sql.SQLObject), - Result: make(map[string]sql.SQLObject), - } - request.Ctx.From = time.Unix(int64(request.Ctx.FromS), 0) - request.Ctx.To = time.Unix(int64(request.Ctx.ToS), 0) - sel, err := planner.Process(&request.Ctx) - if err != nil { - data[id].response = []byte(err.Error()) - return 1 - } - var options []int - if request.Ctx.IsCluster { - options = append(options, sql.STRING_OPT_INLINE_WITH) - } - str, err := sel.String(request.Ctx.CHSqlCtx, options...) - print(str) - print("\n") - if err != nil { - data[id].response = []byte(err.Error()) - return 1 - } - data[id].response = []byte(str) - return 0 -} - -var eng *promql.Engine = promql.NewEngine(promql.EngineOpts{ - Logger: TestLogger{}, - MaxSamples: maxSamples, - Timeout: time.Second * 30, - ActiveQueryTracker: nil, - LookbackDelta: 0, - NoStepSubqueryIntervalFn: nil, - EnableAtModifier: false, - EnableNegativeOffset: false, -}) -var engC = func() *promql.Engine { - return promql.NewEngine(promql.EngineOpts{ - Logger: TestLogger{}, - MaxSamples: maxSamples, - Timeout: time.Second * 30, - ActiveQueryTracker: nil, - LookbackDelta: 0, - NoStepSubqueryIntervalFn: nil, - EnableAtModifier: false, - EnableNegativeOffset: false, - }) -}() - -func getEng() *promql.Engine { - return eng -} - -//export setMaxSamples -func setMaxSamples(maxSpl int) { - maxSamples = maxSpl -} - -//export stats -func stats() { - fmt.Printf("Allocated data: %d\n", len(data)) -} - -//export pqlRangeQuery -func pqlRangeQuery(id uint32, fromMS float64, toMS float64, stepMS float64, optimizable uint32) uint32 { - ctxId := gcContext.GetContextID() - gcContext.SetContext(id) - defer gcContext.SetContext(ctxId) - - return pql(id, data[id], optimizable != 0, int64(fromMS), int64(toMS), int64(stepMS), func() (promql.Query, error) { - queriable := &TestQueryable{id: id, stepMs: int64(stepMS)} - return getEng().NewRangeQuery( - queriable, - nil, - string(data[id].request), - time.Unix(0, int64(fromMS)*1000000), - time.Unix(0, int64(toMS)*1000000), - time.Millisecond*time.Duration(stepMS)) - }) - -} - -//export pqlInstantQuery -func pqlInstantQuery(id uint32, timeMS float64, optimizable uint32) uint32 { - ctxId := gcContext.GetContextID() - gcContext.SetContext(id) - defer gcContext.SetContext(ctxId) - - return pql(id, data[id], optimizable != 0, int64(timeMS-300000), int64(timeMS), 15000, - func() (promql.Query, error) { - queriable := &TestQueryable{id: id, stepMs: 15000} - return getEng().NewInstantQuery( - queriable, - nil, - string(data[id].request), - time.Unix(0, int64(timeMS)*1000000)) - }) -} - -//export pqlSeries -func pqlSeries(id uint32) uint32 { - ctxId := gcContext.GetContextID() - gcContext.SetContext(id) - defer gcContext.SetContext(ctxId) - - queriable := &TestQueryable{id: id, stepMs: 15000} - query, err := getEng().NewRangeQuery( - queriable, - nil, - string(data[id].request), - time.Unix(0, 1), - time.Unix(0, 2), - time.Second) - if err != nil { - data[id].response = wrapError(err) - return 1 - } - data[id].response = []byte(getmatchersJSON(query)) - return 0 -} - -func getmatchersJSON(q promql.Query) string { - var matchersJson = strings.Builder{} - var walk func(node parser.Node, i func(node parser.Node)) - walk = func(node parser.Node, i func(node parser.Node)) { - i(node) - for _, n := range parser.Children(node) { - walk(n, i) - } - } - i := 0 - matchersJson.WriteString("[") - walk(q.Statement(), func(node parser.Node) { - switch n := node.(type) { - case *parser.VectorSelector: - if i != 0 { - matchersJson.WriteString(",") - } - matchersJson.WriteString(matchers2Str(n.LabelMatchers)) - i++ - } - }) - matchersJson.WriteString("]") - return matchersJson.String() -} - -func wrapError(err error) []byte { - return []byte(wrapErrorStr(err)) -} - -func wrapErrorStr(err error) string { - //return fmt.Sprintf(`{"status":"error", "error":%s}`, strconv.Quote(err.Error())) - return err.Error() -} - -func pql(id uint32, c *ctx, optimizable bool, - fromMs int64, toMs int64, stepMs int64, - query func() (promql.Query, error)) uint32 { - rq, err := query() - - if err != nil { - c.response = wrapError(err) - return 1 - } - - var walk func(node parser.Node, i func(node parser.Node)) - walk = func(node parser.Node, i func(node parser.Node)) { - i(node) - for _, n := range parser.Children(node) { - walk(n, i) - } - } - - maxDurationMs := getMaxDurationMs(rq.Statement()) - fromMs -= maxDurationMs - - subsels := strings.Builder{} - subsels.WriteString("{") - if optimizable { - var ( - subselsMap map[string]string - err error - ) - subselsMap, rq, err = optimizeQuery(rq, fromMs, toMs, stepMs) - if err != nil { - c.response = wrapError(err) - return 1 - } - i := 0 - for k, v := range subselsMap { - if i != 0 { - subsels.WriteString(",") - } - subsels.WriteString(fmt.Sprintf(`%s:%s`, strconv.Quote(k), strconv.Quote(v))) - i++ - } - } - subsels.WriteString("}") - - matchersJSON := getmatchersJSON(rq) - - c.response = []byte(fmt.Sprintf(`{"subqueries": %s, "matchers": %s, "fromMs": %d}`, - subsels.String(), - matchersJSON, - fromMs)) - c.onDataLoad = func(c *ctx) { - ctxId := gcContext.GetContextID() - gcContext.SetContext(id) - defer gcContext.SetContext(ctxId) - - res := rq.Exec(context.Background()) - c.response = []byte(writeResponse(res)) - return - } - return 0 -} - -func getMaxDurationMs(q parser.Node) int64 { - maxDurationMs := int64(0) - for _, c := range parser.Children(q) { - _m := getMaxDurationMs(c) - if _m > maxDurationMs { - maxDurationMs = _m - } - } - ms, _ := q.(*parser.MatrixSelector) - if ms != nil && maxDurationMs < ms.Range.Milliseconds() { - return ms.Range.Milliseconds() - } - return maxDurationMs -} - -func optimizeQuery(q promql.Query, fromMs int64, toMs int64, stepMs int64) (map[string]string, promql.Query, error) { - appliableNodes := findAppliableNodes(q.Statement(), nil) - var err error - subsels := make(map[string]string) - for _, m := range appliableNodes { - fmt.Println(m) - opt := m.optimizer - opt = &promql2.FinalizerOptimizer{ - SubOptimizer: opt, - } - opt, err = promql2.PlanOptimize(m.node, opt) - if err != nil { - return nil, nil, err - } - planner, err := opt.Optimize(m.node) - if err != nil { - return nil, nil, err - } - fakeMetric := fmt.Sprintf("fake_metric_%d", time.Now().UnixNano()) - swapChild(m.parent, m.node, &parser.VectorSelector{ - Name: fakeMetric, - OriginalOffset: 0, - Offset: 0, - Timestamp: nil, - StartOrEnd: 0, - LabelMatchers: []*labels.Matcher{ - { - Type: labels.MatchEqual, - Name: "__name__", - Value: fakeMetric, - }, - }, - UnexpandedSeriesSet: nil, - Series: nil, - PosRange: parser.PositionRange{}, - }) - sel, err := planner.Process(&shared2.PlannerContext{ - IsCluster: false, - From: time.Unix(0, fromMs*1000000), - To: time.Unix(0, toMs*1000000), - Step: time.Millisecond * 15000, /*time.Duration(stepMs)*/ - TimeSeriesTable: "time_series", - TimeSeriesDistTable: "time_series_dist", - TimeSeriesGinTable: "time_series_gin", - MetricsTable: "metrics_15s", - MetricsDistTable: "metrics_15s_dist", - }) - if err != nil { - return nil, nil, err - } - strSel, err := sel.String(&sql.Ctx{ - Params: map[string]sql.SQLObject{}, - Result: map[string]sql.SQLObject{}, - }) - if err != nil { - return nil, nil, err - } - subsels[fakeMetric] = strSel - } - return subsels, q, nil -} - -//export onDataLoad -func onDataLoad(idx uint32) { - data[idx].onDataLoad(data[idx]) -} - -func writeResponse(res *promql.Result) string { - if res.Err != nil { - return wrapErrorStr(res.Err) - } - switch res.Value.Type() { - case parser.ValueTypeMatrix: - m, err := res.Matrix() - if err != nil { - return wrapErrorStr(err) - } - return writeMatrix(m) - case parser.ValueTypeVector: - v, err := res.Vector() - if err != nil { - return wrapErrorStr(err) - } - return writeVector(v) - } - return wrapErrorStr(fmt.Errorf("result type not supported")) -} - -func writeMatrix(m promql.Matrix) string { - jsonBuilder := strings.Builder{} - jsonBuilder.WriteString(`{"status": "success", "data": {"resultType":"matrix","result":[`) - for i, s := range m { - if i != 0 { - jsonBuilder.WriteString(",") - } - jsonBuilder.WriteString(`{"metric": {`) - for j, l := range s.Metric { - if j != 0 { - jsonBuilder.WriteString(",") - } - jsonBuilder.WriteString(fmt.Sprintf("%s:%s", strconv.Quote(l.Name), strconv.Quote(l.Value))) - } - jsonBuilder.WriteString(`}, "values": [`) - for j, v := range s.Points { - if j != 0 { - jsonBuilder.WriteString(",") - } - jsonBuilder.WriteString(fmt.Sprintf("[%d,\"%f\"]", v.T/1000, v.V)) - } - jsonBuilder.WriteString(`]}`) - } - jsonBuilder.WriteString(`]}}`) - return jsonBuilder.String() -} - -func writeVector(v promql.Vector) string { - jsonBuilder := strings.Builder{} - jsonBuilder.WriteString(`{"status": "success", "data": {"resultType":"vector","result":[`) - for i, s := range v { - if i != 0 { - jsonBuilder.WriteString(",") - } - jsonBuilder.WriteString(`{"metric": {`) - for j, l := range s.Metric { - if j != 0 { - jsonBuilder.WriteString(",") - } - jsonBuilder.WriteString(fmt.Sprintf("%s:%s", strconv.Quote(l.Name), strconv.Quote(l.Value))) - } - jsonBuilder.WriteString(fmt.Sprintf(`}, "value": [%d,"%f"]}`, s.T/1000, s.V)) - } - jsonBuilder.WriteString(`]}}`) - return jsonBuilder.String() -} - -func main() { -} - -func getOptimizer(n parser.Node) promql2.IOptimizer { - for _, f := range promql2.Optimizers { - opt := f() - if opt.IsAppliable(n) { - return opt - } - } - return nil -} - -func isRate(node parser.Node) (bool, bool) { - opt := getOptimizer(node) - if opt == nil { - return false, true - } - return true, false -} - -type MatchNode struct { - node parser.Node - parent parser.Node - optimizer promql2.IOptimizer -} - -func findAppliableNodes(root parser.Node, parent parser.Node) []MatchNode { - var res []MatchNode - optimizer := getOptimizer(root) - if optimizer != nil { - res = append(res, MatchNode{ - node: root, - parent: parent, - optimizer: optimizer, - }) - return res - } - for _, n := range parser.Children(root) { - res = append(res, findAppliableNodes(n, root)...) - } - return res -} - -func swapChild(node parser.Node, child parser.Node, newChild parser.Expr) { - // For some reasons these switches have significantly better performance than interfaces - switch n := node.(type) { - case *parser.EvalStmt: - n.Expr = newChild - case parser.Expressions: - for i, e := range n { - if e.String() == child.String() { - n[i] = newChild - } - } - case *parser.AggregateExpr: - if n.Expr == nil && n.Param == nil { - return - } else if n.Expr == nil { - n.Param = newChild - } else if n.Param == nil { - n.Expr = newChild - } else { - if n.Expr.String() == child.String() { - n.Expr = newChild - } else { - n.Param = newChild - } - } - case *parser.BinaryExpr: - if n.LHS.String() == child.String() { - n.LHS = newChild - } else if n.RHS.String() == child.String() { - n.RHS = newChild - } - case *parser.Call: - for i, e := range n.Args { - if e.String() == child.String() { - n.Args[i] = newChild - } - } - case *parser.SubqueryExpr: - n.Expr = newChild - case *parser.ParenExpr: - n.Expr = newChild - case *parser.UnaryExpr: - n.Expr = newChild - case *parser.MatrixSelector: - n.VectorSelector = newChild - case *parser.StepInvariantExpr: - n.Expr = newChild - } -} - -func getChildren(e parser.Node) []parser.Node { - return parser.Children(e) -} - -type TestLogger struct{} - -func (t TestLogger) Log(keyvals ...interface{}) error { - fmt.Print(keyvals...) - fmt.Print("\n") - return nil -} - -type TestQueryable struct { - id uint32 - stepMs int64 -} - -func (t TestQueryable) Querier(ctx context.Context, mint, maxt int64) (storage.Querier, error) { - sets := make(map[string][]byte) - r := BinaryReader{buffer: data[t.id].request} - for r.i < uint32(len(data[t.id].request)) { - sets[r.ReadString()] = r.ReadByteArray() - } - return &TestQuerier{sets: sets, stepMs: t.stepMs}, nil -} - -type TestQuerier struct { - sets map[string][]byte - stepMs int64 -} - -func (t TestQuerier) LabelValues(name string, matchers ...*labels.Matcher) ([]string, storage.Warnings, error) { - return nil, nil, nil -} - -func (t TestQuerier) LabelNames(matchers ...*labels.Matcher) ([]string, storage.Warnings, error) { - return nil, nil, nil -} - -func (t TestQuerier) Close() error { - return nil -} - -func (t TestQuerier) Select(sortSeries bool, hints *storage.SelectHints, matchers ...*labels.Matcher) storage.SeriesSet { - strMatchers := matchers2Str(matchers) - return &TestSeriesSet{ - data: t.sets[strMatchers], - reader: BinaryReader{buffer: t.sets[strMatchers]}, - stepMs: t.stepMs, - } -} - -type TestSeriesSet struct { - data []byte - reader BinaryReader - stepMs int64 -} - -func (t *TestSeriesSet) Next() bool { - return t.reader.i < uint32(len(t.data)) -} - -func (t *TestSeriesSet) At() storage.Series { - res := &TestSeries{ - i: 0, - stepMs: t.stepMs, - } - res.labels = t.reader.ReadLabelsTuple() - res.data = t.reader.ReadPointsArrayRaw() - res.reset() - return res -} - -func (t *TestSeriesSet) Err() error { - return nil -} - -func (t *TestSeriesSet) Warnings() storage.Warnings { - return nil -} - -type TestSeries struct { - data []byte - stepMs int64 - - labels labels.Labels - tsMs int64 - val float64 - lastValTs int64 - i int - - state int -} - -func (t *TestSeries) reset() { - if len(t.data) == 0 { - return - } - t.tsMs = *(*int64)(unsafe.Pointer(&t.data[0])) - t.val = *(*float64)(unsafe.Pointer(&t.data[t.i*16+8])) -} - -func (t *TestSeries) Next() bool { - if t.i*16 >= len(t.data) { - return false - } - ts := *(*int64)(unsafe.Pointer(&t.data[t.i*16])) - if t.state == 1 { - t.tsMs += t.stepMs - if t.tsMs >= ts { - t.state = 0 - } else if t.lastValTs+300000 < t.tsMs { - t.state = 0 - } - } - if t.state == 0 { - t.tsMs = ts - t.val = *(*float64)(unsafe.Pointer(&t.data[t.i*16+8])) - t.lastValTs = t.tsMs - t.i++ - t.state = 1 - } - return true -} - -func (t *TestSeries) Seek(tmMS int64) bool { - for t.i = 0; t.i*16 < len(t.data); t.i++ { - ms := *(*int64)(unsafe.Pointer(&t.data[t.i*16])) - if ms == tmMS { - t.tsMs = ms - t.val = *(*float64)(unsafe.Pointer(&t.data[t.i*16+8])) - t.i++ - return true - } - if ms > tmMS { - t.i-- - if t.i < 0 { - t.i = 0 - } - t.tsMs = ms - t.val = *(*float64)(unsafe.Pointer(&t.data[t.i*16+8])) - t.i++ - return true - } - } - return false -} - -func (t *TestSeries) At() (int64, float64) { - return t.tsMs, t.val -} - -func (t *TestSeries) Err() error { - return nil -} - -func (t *TestSeries) Labels() labels.Labels { - return t.labels -} - -func (t *TestSeries) Iterator() chunkenc.Iterator { - return t -} - -type BinaryReader struct { - buffer []byte - i uint32 -} - -func (b *BinaryReader) ReadULeb32() uint32 { - var res uint32 - i := uint32(0) - for ; b.buffer[b.i+i]&0x80 == 0x80; i++ { - res |= uint32(b.buffer[b.i+i]&0x7f) << (i * 7) - } - res |= uint32(b.buffer[b.i+i]&0x7f) << (i * 7) - b.i += i + 1 - return res -} - -func (b *BinaryReader) ReadLabelsTuple() labels.Labels { - ln := b.ReadULeb32() - res := make(labels.Labels, ln) - for i := uint32(0); i < ln; i++ { - ln := b.ReadULeb32() - res[i].Name = string(b.buffer[b.i : b.i+ln]) - b.i += ln - ln = b.ReadULeb32() - res[i].Value = string(b.buffer[b.i : b.i+ln]) - b.i += ln - } - return res -} - -func (b *BinaryReader) ReadPointsArrayRaw() []byte { - ln := b.ReadULeb32() - res := b.buffer[b.i : b.i+(ln*16)] - b.i += ln * 16 - return res -} - -func (b *BinaryReader) ReadString() string { - ln := b.ReadULeb32() - res := string(b.buffer[b.i : b.i+ln]) - b.i += ln - return res -} - -func (b *BinaryReader) ReadByteArray() []byte { - ln := b.ReadULeb32() - res := b.buffer[b.i : b.i+ln] - b.i += ln - return res -} - -func matchers2Str(labelMatchers []*labels.Matcher) string { - matchersJson := strings.Builder{} - matchersJson.WriteString("[") - for j, m := range labelMatchers { - if j != 0 { - matchersJson.WriteString(",") - } - matchersJson.WriteString(fmt.Sprintf(`[%s,"%s",%s]`, - strconv.Quote(m.Name), - m.Type, - strconv.Quote(m.Value))) - } - matchersJson.WriteString("]") - return matchersJson.String() -} - -type pqlRequest struct { - optimizable bool - body string -} - -func (p *pqlRequest) Read(body []byte) { - r := BinaryReader{buffer: body} - p.optimizable = r.ReadULeb32() != 0 - p.body = r.ReadString() - if !p.optimizable { - return - } -} diff --git a/wasm_parts/main.js b/wasm_parts/main.js deleted file mode 100644 index 0ef187a6..00000000 --- a/wasm_parts/main.js +++ /dev/null @@ -1,303 +0,0 @@ -require('./wasm_exec') -const { join } = require('path') -const WASM_URL = join(__dirname, 'main.wasm.gz') -const fs = require('fs') -const { gunzipSync } = require('zlib') - -class WasmError extends Error {} -module.exports.WasmError = WasmError - -let counter = 1 - -const getWasm = (() => { - const _Go = Go - var go = new _Go(); - let wasm = null - async function init () { - go = new _Go(); - const _wasm = await WebAssembly.instantiate( - gunzipSync(fs.readFileSync(WASM_URL)), go.importObject) - go.run(_wasm.instance) - wasm = _wasm.instance - wasm.exports.setMaxSamples(process.env.ADVANCED_PROMETHEUS_MAX_SAMPLES || 5000000) - wasm.exportsWrap = Object.fromEntries( - Object.entries(wasm.exports).map(([_k, _v]) => { - return [_k, (...args) => { - const _wasm = wasm - try { - return _wasm.exports[_k].bind(_wasm)(...args) - } catch (e) { - _wasm === wasm && init() - throw e - } - }] - }) - ) - } - init() - return () => { - return wasm - } -})() - -const newId = () => { - const id = counter - counter = (counter + 1) & 0xFFFFFFFF - return id -} - -/** - * - * @param query {string} - * @param startMs {number} - * @param endMs {number} - * @param stepMs {number} - * @param getData {function} - * @returns {Promise<string>} - */ -module.exports.pqlRangeQuery = async (query, startMs, endMs, stepMs, getData) => { - const _wasm = getWasm() - const start = startMs || Date.now() - 300000 - const end = endMs || Date.now() - const step = stepMs || 15000 - return await pql(query, - (ctx) => _wasm.exportsWrap.pqlRangeQuery(ctx.id, start, end, step, process.env.EXPERIMENTAL_PROMQL_OPTIMIZE ? 1 : 0), - (matchers, subq, startMs) => getData(matchers, startMs, end, subq)) -} - -/** - * - * @param query {string} - * @param timeMs {number} - * @param getData {function} - * @returns {Promise<string>} - */ -module.exports.pqlInstantQuery = async (query, timeMs, getData) => { - const time = timeMs || Date.now() - const _wasm = getWasm() - const start = time - 300000 - return await pql(query, - (ctx) => _wasm.exportsWrap.pqlInstantQuery(ctx.id, time, process.env.EXPERIMENTAL_PROMQL_OPTIMIZE ? 1 : 0), - (matchers, subq, start) => getData(matchers, start, time, subq)) -} - -module.exports.pqlMatchers = (query) => { - const _wasm = getWasm() - const id = newId() - const ctx = new Ctx(id, _wasm) - ctx.create() - try { - ctx.write(query) - const res1 = _wasm.exportsWrap.pqlSeries(id) - if (res1 !== 0) { - throw new WasmError(ctx.read()) - } - /** @type {[[[string]]]} */ - const matchersObj = JSON.parse(ctx.read()) - return matchersObj - } finally { - ctx.destroy() - } -} - -/** - * - * @param request {{ - * Request: string, - * Ctx: { - * IsCluster: boolean, - * OrgID: string, - * FromS: number, - * ToS: number, - * TimeSeriesGinTableName: string, - * SamplesTableName: string, - * TimeSeriesTableName: string, - * TimeSeriesDistTableName: string, - * Metrics15sTableName: string, - * TracesAttrsTable: string, - * TracesAttrsDistTable: string, - * TracesTable: string, - * TracesDistTable: string - * }}} - * @returns {String} - * @constructor - */ -module.exports.TranspileTraceQL = (request) => { - let _ctx - try { - const id = newId() - const _wasm = getWasm() - _ctx = new Ctx(id, _wasm) - _ctx.create() - _ctx.write(JSON.stringify(request)) - let res = _wasm.exportsWrap.transpileTraceQL(id) - if (res !== 0) { - throw new WasmError(_ctx.read()) - } - res = _ctx.read() - return res - } finally { - _ctx && _ctx.destroy() - } -} - -/** - * - * @param query {string} - * @param wasmCall {function} - * @param getData {function} - * @returns {Promise<string>} - */ -const pql = async (query, wasmCall, getData) => { - const reqId = newId() - const _wasm = getWasm() - const ctx = new Ctx(reqId, _wasm) - try { - ctx.create() - ctx.write(query) - const res1 = wasmCall(ctx) - if (res1 !== 0) { - throw new WasmError(ctx.read()) - } - - const matchersObj = JSON.parse(ctx.read()) - - const matchersResults = await Promise.all( - matchersObj.matchers.map(async (matchers, i) => { - const data = await getData(matchers, matchersObj.subqueries, matchersObj.fromMs) - return { matchers, data } - })) - - const writer = new Uint8ArrayWriter(new Uint8Array(1024)) - for (const { matchers, data } of matchersResults) { - writer.writeString(JSON.stringify(matchers)) - writer.writeBytes([data]) - } - ctx.write(writer.buffer()) - _wasm.exportsWrap.onDataLoad(reqId) - return ctx.read() - } finally { - ctx && ctx.destroy() - } -} -class Ctx { - constructor (id, wasm) { - this.wasm = wasm - this.id = id - this.created = false - } - - create () { - try { - this.wasm.exportsWrap.createCtx(this.id) - this.created = true - } catch (err) { - throw err - } - } - - destroy () { - try { - if (this.created) this.wasm.exportsWrap.dealloc(this.id) - } catch (err) { - throw err - } - } - - /** - * - * @param data {Uint8Array | string} - */ - write (data) { - if (typeof data === 'string') { - data = (new TextEncoder()).encode(data) - } - this.wasm.exportsWrap.alloc(this.id, data.length) - const ptr = this.wasm.exportsWrap.alloc(this.id, data.length) - new Uint8Array(this.wasm.exports.memory.buffer).set(data, ptr) - } - - /** - * @returns {String} - */ - read() { - const [resPtr, resLen] = [ - this.wasm.exportsWrap.getCtxResponse(this.id), - this.wasm.exportsWrap.getCtxResponseLen(this.id) - ] - return new TextDecoder().decode(new Uint8Array(this.wasm.exports.memory.buffer).subarray(resPtr, resPtr + resLen)) - } -} - -class Uint8ArrayWriter { - /** - * - * @param buf {Uint8Array} - */ - constructor (buf) { - this.buf = buf - this.i = 0 - } - - maybeGrow (len) { - for (;this.i + len > this.buf.length;) { - const _buf = new Uint8Array(this.buf.length + 1024 * 1024) - _buf.set(this.buf) - this.buf = _buf - } - } - - /** - * - * @param n {number} - */ - writeULeb (n) { - this.maybeGrow(9) - let _n = n - if (n === 0) { - this.buf[this.i] = 0 - this.i++ - return - } - while (_n > 0) { - let part = _n & 0x7f - _n >>= 7 - if (_n > 0) { - part |= 0x80 - } - this.buf[this.i] = part - this.i++ - } - } - - /** - * - * @param str {string} - */ - writeString (str) { - const bStr = (new TextEncoder()).encode(str) - this.writeULeb(bStr.length) - this.maybeGrow(bStr.length) - this.buf.set(bStr, this.i) - this.i += bStr.length - return this - } - - /** - * - * @param buf {Uint8Array[]} - */ - writeBytes (buf) { - for (const b of buf) { - this.writeULeb(b.length) - this.maybeGrow(b.length) - this.buf.set(b, this.i) - this.i += b.length - } - return this - } - - buffer () { - return this.buf.subarray(0, this.i) - } -} diff --git a/wasm_parts/main.wasm.gz b/wasm_parts/main.wasm.gz deleted file mode 100644 index eba7be72..00000000 Binary files a/wasm_parts/main.wasm.gz and /dev/null differ diff --git a/wasm_parts/promql/aggregate.go b/wasm_parts/promql/aggregate.go deleted file mode 100644 index 0fd2ed15..00000000 --- a/wasm_parts/promql/aggregate.go +++ /dev/null @@ -1,71 +0,0 @@ -package promql - -import ( - "fmt" - "github.com/prometheus/prometheus/promql/parser" - "wasm_parts/promql/planners" - "wasm_parts/promql/shared" -) - -type AggregateOptimizer struct { - WithLabelsIn string - WithLabelsOut string - - subOptimizer IOptimizer -} - -func (a *AggregateOptimizer) IsAppliable(node parser.Node) bool { - aggExpr, ok := node.(*parser.AggregateExpr) - if !ok { - return false - } - if aggExpr.Op != parser.SUM { - return false - } - return GetAppliableOptimizer(aggExpr.Expr, append(Optimizers, VectorSelectorOptimizerFactory)) != nil -} - -func (a *AggregateOptimizer) PlanOptimize(node parser.Node) error { - aggExpr := node.(*parser.AggregateExpr) - a.subOptimizer = GetAppliableOptimizer(aggExpr.Expr, append(Optimizers, VectorSelectorOptimizerFactory)) - return a.subOptimizer.PlanOptimize(node) -} - -func (a *AggregateOptimizer) Optimize(node parser.Node) (shared.RequestPlanner, error) { - aggExpr := node.(*parser.AggregateExpr) - planner, err := a.subOptimizer.Optimize(aggExpr.Expr) - if err != nil { - return nil, err - } - withLabelsIn := a.WithLabelsIn - if withLabelsIn == "" { - planner = &planners.LabelsInitPlanner{ - Main: planner, - FingerprintsAlias: "fp_sel", - } - withLabelsIn = "labels" - } - if a.WithLabelsOut == "" { - return nil, fmt.Errorf("AggregateOptimizer.WithLabelsOut is empty") - } - byWithout := "by" - if aggExpr.Without { - byWithout = "without" - } - planner = &planners.ByWithoutPlanner{ - Main: planner, - FingerprintWithName: withLabelsIn, - FingerprintsOutName: a.WithLabelsOut, - ByWithout: byWithout, - Labels: aggExpr.Grouping, - } - planner = &planners.SumPlanner{ - Main: planner, - LabelsAlias: a.WithLabelsOut, - } - return planner, nil -} - -func (a *AggregateOptimizer) Children() []IOptimizer { - return []IOptimizer{a.subOptimizer} -} diff --git a/wasm_parts/promql/finalize.go b/wasm_parts/promql/finalize.go deleted file mode 100644 index d15bcf29..00000000 --- a/wasm_parts/promql/finalize.go +++ /dev/null @@ -1,45 +0,0 @@ -package promql - -import ( - "github.com/prometheus/prometheus/promql/parser" - "wasm_parts/promql/planners" - "wasm_parts/promql/shared" -) - -type FinalizerOptimizer struct { - LabelsIn string - SubOptimizer IOptimizer -} - -func (f *FinalizerOptimizer) IsAppliable(node parser.Node) bool { - return false -} - -func (f *FinalizerOptimizer) Optimize(node parser.Node) (shared.RequestPlanner, error) { - planner, err := f.SubOptimizer.Optimize(node) - if err != nil { - return nil, err - } - labelsIn := f.LabelsIn - if labelsIn == "" { - planner = &planners.LabelsInitPlanner{ - Main: planner, - FingerprintsAlias: "fp_sel", - } - labelsIn = "labels" - } - - planner = &planners.FinalizePlanner{ - LabelsAlias: labelsIn, - Main: planner, - } - return planner, nil -} - -func (f *FinalizerOptimizer) PlanOptimize(node parser.Node) error { - return f.SubOptimizer.PlanOptimize(node) -} - -func (f *FinalizerOptimizer) Children() []IOptimizer { - return []IOptimizer{f.SubOptimizer} -} diff --git a/wasm_parts/promql/optimize.go b/wasm_parts/promql/optimize.go deleted file mode 100644 index 2ea382ea..00000000 --- a/wasm_parts/promql/optimize.go +++ /dev/null @@ -1,37 +0,0 @@ -package promql - -import ( - "fmt" - "github.com/prometheus/prometheus/promql/parser" -) - -func PlanOptimize(node parser.Node, optimizer IOptimizer) (IOptimizer, error) { - err := optimizer.PlanOptimize(node) - if err != nil { - return nil, err - } - - var checkLabelAliases func(opt IOptimizer, i int) int - checkLabelAliases = func(opt IOptimizer, i int) int { - var _i int - for _, c := range opt.Children() { - _i = checkLabelAliases(c, i) - } - switch opt.(type) { - case *AggregateOptimizer: - if _i != 0 { - opt.(*AggregateOptimizer).WithLabelsIn = fmt.Sprintf("labels_", _i) - } - opt.(*AggregateOptimizer).WithLabelsOut = fmt.Sprintf("labels_%d", _i+1) - _i++ - case *FinalizerOptimizer: - if _i != 0 { - opt.(*FinalizerOptimizer).LabelsIn = fmt.Sprintf("labels_%d", _i) - } - _i++ - } - return _i - } - checkLabelAliases(optimizer, 0) - return optimizer, nil -} diff --git a/wasm_parts/promql/planners/aggregate.go b/wasm_parts/promql/planners/aggregate.go deleted file mode 100644 index a1f6cf0d..00000000 --- a/wasm_parts/promql/planners/aggregate.go +++ /dev/null @@ -1,48 +0,0 @@ -package planners - -import ( - "fmt" - "wasm_parts/promql/shared" - sql "wasm_parts/sql_select" -) - -type SumPlanner struct { - Main shared.RequestPlanner - LabelsAlias string -} - -func (s *SumPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { - main, err := s.Main.Process(ctx) - if err != nil { - return nil, err - } - - var withLabels *sql.With - for _, w := range main.GetWith() { - if w.GetAlias() == s.LabelsAlias { - withLabels = w - break - } - } - if withLabels == nil { - return nil, fmt.Errorf("labels subrequest not found") - } - withMain := sql.NewWith(main, "pre_sum") - - res := sql.NewSelect().With(withMain). - Select( - sql.NewSimpleCol(withLabels.GetAlias()+".new_fingerprint", "fingerprint"), - sql.NewSimpleCol("pre_sum.timestamp_ms", "timestamp_ms"), - sql.NewSimpleCol("sum(pre_sum.value)", "value")). - From(sql.NewWithRef(withMain)). - Join(sql.NewJoin( - "ANY LEFT", - sql.NewWithRef(withLabels), - sql.Eq( - sql.NewRawObject("pre_sum.fingerprint"), - sql.NewRawObject(withLabels.GetAlias()+".fingerprint")))). - GroupBy( - sql.NewRawObject(withLabels.GetAlias()+".new_fingerprint"), - sql.NewRawObject("pre_sum.timestamp_ms")) - return res, nil -} diff --git a/wasm_parts/promql/planners/by_without.go b/wasm_parts/promql/planners/by_without.go deleted file mode 100644 index de38b83e..00000000 --- a/wasm_parts/promql/planners/by_without.go +++ /dev/null @@ -1,59 +0,0 @@ -package planners - -import ( - "fmt" - "strings" - "wasm_parts/promql/shared" - sql "wasm_parts/sql_select" -) - -type ByWithoutPlanner struct { - Main shared.RequestPlanner - FingerprintWithName string - FingerprintsOutName string - ByWithout string - Labels []string -} - -func (b *ByWithoutPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { - main, err := b.Main.Process(ctx) - if err != nil { - return nil, err - } - var fp *sql.With - withs := main.GetWith() - for _, w := range withs { - if w.GetAlias() == b.FingerprintWithName { - fp = w - break - } - } - if fp == nil { - return nil, fmt.Errorf("fingerprints subrequest not found") - } - labelsCol := sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { - cond := "IN" - if b.ByWithout == "without" { - cond = "NOT IN" - } - values := make([]string, len(b.Labels)) - var err error - for i, l := range b.Labels { - values[i], err = sql.NewStringVal(l).String(ctx, options...) - if err != nil { - return "", err - } - } - return fmt.Sprintf("mapFilter((k,v) -> k %s (%s), labels)", cond, strings.Join(values, ",")), nil - }) - newFpCol := "cityHash64(arraySort(arrayZip(mapKeys(labels), mapValues(labels))))" - newFp := sql.NewSelect(). - Select( - sql.NewSimpleCol(fp.GetAlias()+".new_fingerprint", "fingerprint"), - sql.NewCol(labelsCol, "labels"), - sql.NewSimpleCol(newFpCol, "new_fingerprint"), - ). - From(sql.NewWithRef(fp)) - withNewFp := sql.NewWith(newFp, b.FingerprintsOutName) - return main.AddWith(withNewFp), nil -} diff --git a/wasm_parts/promql/planners/finalize.go b/wasm_parts/promql/planners/finalize.go deleted file mode 100644 index f3064f28..00000000 --- a/wasm_parts/promql/planners/finalize.go +++ /dev/null @@ -1,47 +0,0 @@ -package planners - -import ( - "fmt" - "wasm_parts/promql/shared" - sql "wasm_parts/sql_select" -) - -type FinalizePlanner struct { - LabelsAlias string - Main shared.RequestPlanner -} - -func (f *FinalizePlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { - main, err := f.Main.Process(ctx) - if err != nil { - return nil, err - } - - var withLabels *sql.With - for _, w := range main.GetWith() { - if w.GetAlias() == f.LabelsAlias { - withLabels = w - break - } - } - - if withLabels == nil { - return nil, fmt.Errorf("FinalizePlanner.Process: %s CTE not found", f.LabelsAlias) - } - - withMain := sql.NewWith(main, "pre_final") - res := sql.NewSelect().With(withMain).Select(withMain). - Select( - sql.NewSimpleCol(withLabels.GetAlias()+".labels", "labels"), - sql.NewSimpleCol("arraySort(groupArray((pre_final.timestamp_ms, pre_final.value)))", "values"), - ).From(sql.NewWithRef(withMain)). - //AndWhere(sql.Neq(sql.NewRawObject("pre_final.value"), sql.NewIntVal(0))). - Join(sql.NewJoin( - "ANY LEFT", - sql.NewWithRef(withLabels), - sql.Eq( - sql.NewRawObject("pre_final.fingerprint"), - sql.NewRawObject(withLabels.GetAlias()+".new_fingerprint")))). - GroupBy(sql.NewRawObject(withLabels.GetAlias() + ".labels")) - return res, nil -} diff --git a/wasm_parts/promql/planners/labels_init.go b/wasm_parts/promql/planners/labels_init.go deleted file mode 100644 index 35c060fc..00000000 --- a/wasm_parts/promql/planners/labels_init.go +++ /dev/null @@ -1,48 +0,0 @@ -package planners - -import ( - "fmt" - "wasm_parts/promql/shared" - sql "wasm_parts/sql_select" -) - -type LabelsInitPlanner struct { - Main shared.RequestPlanner - FingerprintsAlias string -} - -func (l *LabelsInitPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { - main, err := l.Main.Process(ctx) - if err != nil { - return nil, err - } - - var withFp *sql.With - for _, w := range main.GetWith() { - if w.GetAlias() == l.FingerprintsAlias { - withFp = w - break - } - } - - if withFp == nil { - return nil, fmt.Errorf("fingerprints subrequest not found") - } - - labelsCol := "mapFromArrays(" + - "arrayMap(x -> x.1, JSONExtractKeysAndValues(time_series.labels, 'String') as ts_kv), " + - "arrayMap(x -> x.2, ts_kv))" - - labelsSubSel := sql.NewSelect().Select( - sql.NewSimpleCol("fingerprint", "fingerprint"), - sql.NewSimpleCol(labelsCol, "labels"), - sql.NewSimpleCol("fingerprint", "new_fingerprint")). - From(sql.NewSimpleCol(ctx.TimeSeriesTable, "time_series")). - AndWhere( - sql.Ge(sql.NewRawObject("date"), sql.NewStringVal(ctx.From.Format("2006-01-02"))), - sql.Le(sql.NewRawObject("date"), sql.NewStringVal(ctx.To.Format("2006-01-02"))), - sql.NewIn(sql.NewRawObject("fingerprint"), sql.NewWithRef(withFp))) - withLabelsSubSel := sql.NewWith(labelsSubSel, "labels") - - return main.AddWith(withLabelsSubSel), nil -} diff --git a/wasm_parts/promql/planners/metrics_extend.go b/wasm_parts/promql/planners/metrics_extend.go deleted file mode 100644 index 52e1916c..00000000 --- a/wasm_parts/promql/planners/metrics_extend.go +++ /dev/null @@ -1,46 +0,0 @@ -package planners - -import ( - "fmt" - "wasm_parts/promql/shared" - sql "wasm_parts/sql_select" -) - -type MetricsExtendPlanner struct { - Main shared.RequestPlanner -} - -func (m *MetricsExtendPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { - main, err := m.Main.Process(ctx) - if err != nil { - return nil, err - } - extendCnt := 300000 / ctx.Step.Milliseconds() - if extendCnt < 1 { - return main, nil - } - withMain := sql.NewWith(main, "pre_extend") - extendedCol := sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { - return fmt.Sprintf( - "argMaxIf(value, timestamp_ms, pre_extend.original = 1) OVER ("+ - "PARTITION BY fingerprint ORDER BY timestamp_ms ROWS BETWEEN %d PRECEDING AND CURRENT ROW"+ - ")", extendCnt), nil - }) - origCol := sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { - return fmt.Sprintf( - "max(original) OVER ("+ - "PARTITION BY fingerprint ORDER BY timestamp_ms ROWS BETWEEN %d PRECEDING AND CURRENT ROW"+ - ")", extendCnt), nil - }) - extend := sql.NewSelect().With(withMain). - Select( - sql.NewSimpleCol("fingerprint", "fingerprint"), - sql.NewSimpleCol("timestamp_ms", "timestamp_ms"), - sql.NewCol(extendedCol, "value"), - sql.NewCol(origCol, "original")). - From(sql.NewWithRef(withMain)) - withExtend := sql.NewWith(extend, "extend") - return sql.NewSelect().With(withExtend).Select(sql.NewRawObject("*")). - From(sql.NewWithRef(withExtend)). - AndWhere(sql.Eq(sql.NewRawObject("original"), sql.NewIntVal(1))), nil -} diff --git a/wasm_parts/promql/planners/metrics_rate.go b/wasm_parts/promql/planners/metrics_rate.go deleted file mode 100644 index 4a472c42..00000000 --- a/wasm_parts/promql/planners/metrics_rate.go +++ /dev/null @@ -1,77 +0,0 @@ -package planners - -import ( - "fmt" - "time" - "wasm_parts/promql/shared" - sql "wasm_parts/sql_select" -) - -type RatePlanner struct { - Main shared.RequestPlanner - Duration time.Duration -} - -func (m *RatePlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { - main, err := m.Main.Process(ctx) - if err != nil { - return nil, err - } - rateCnt := m.Duration.Milliseconds() / ctx.Step.Milliseconds() - if rateCnt < 1 { - rateCnt = 1 - } - withMain := sql.NewWith(main, "pre_rate") - lastCol := sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { - return fmt.Sprintf( - "argMax(value, timestamp_ms) OVER ("+ - "PARTITION BY fingerprint ORDER BY timestamp_ms ROWS BETWEEN %d PRECEDING AND CURRENT ROW"+ - ")", rateCnt), nil - }) - firstCol := sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { - return fmt.Sprintf( - "argMin(value, timestamp_ms) OVER ("+ - "PARTITION BY fingerprint ORDER BY timestamp_ms ROWS BETWEEN %d PRECEDING AND CURRENT ROW"+ - ")", rateCnt), nil - }) - resetCol := sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { - return fmt.Sprintf( - "if(value < (any(value) OVER (" + - "PARTITION BY fingerprint ORDER BY timestamp_ms ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING" + - ") as lastValue), lastValue, 0)"), nil - }) - reset := sql.NewSelect().With(withMain). - Select( - sql.NewSimpleCol("fingerprint", "fingerprint"), - sql.NewSimpleCol("timestamp_ms", "timestamp_ms"), - sql.NewCol(resetCol, "reset"), - sql.NewSimpleCol("value", "value")). - From(sql.NewWithRef(withMain)) - withReset := sql.NewWith(reset, "pre_reset") - resetColSum := sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { - _rateCnt := rateCnt - 1 - if rateCnt <= 1 { - _rateCnt = 1 - } - return fmt.Sprintf( - "sum(reset) OVER ("+ - "PARTITION BY fingerprint ORDER BY timestamp_ms ROWS BETWEEN %d PRECEDING AND CURRENT ROW"+ - ")", _rateCnt), nil - }) - extend := sql.NewSelect().With(withReset). - Select( - sql.NewSimpleCol("fingerprint", "fingerprint"), - sql.NewSimpleCol("timestamp_ms", "timestamp_ms"), - sql.NewCol(lastCol, "last"), - sql.NewCol(firstCol, "first"), - sql.NewCol(resetColSum, "reset"), - sql.NewSimpleCol(fmt.Sprintf("(last - first + reset) / %f", m.Duration.Seconds()), "_value")). - From(sql.NewWithRef(withReset)) - withExtend := sql.NewWith(extend, "rate") - return sql.NewSelect(). - With(withExtend). - Select(sql.NewSimpleCol("fingerprint", "fingerprint"), - sql.NewSimpleCol("timestamp_ms", "timestamp_ms"), - sql.NewSimpleCol("_value", "value")). - From(sql.NewWithRef(withExtend)), nil -} diff --git a/wasm_parts/promql/planners/metrics_raw_init.go b/wasm_parts/promql/planners/metrics_raw_init.go deleted file mode 100644 index 4cc233c3..00000000 --- a/wasm_parts/promql/planners/metrics_raw_init.go +++ /dev/null @@ -1,37 +0,0 @@ -package planners - -import ( - "fmt" - "wasm_parts/promql/shared" - sql "wasm_parts/sql_select" -) - -type MetricsInitPlanner struct { - ValueCol sql.SQLObject - Fingerprint shared.RequestPlanner -} - -func (m *MetricsInitPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { - fpReq, err := m.Fingerprint.Process(ctx) - if err != nil { - return nil, err - } - withFpReq := sql.NewWith(fpReq, "fp_sel") - if m.ValueCol == nil { - m.ValueCol = sql.NewRawObject("argMaxMerge(last)") - } - tsNsCol := sql.NewCustomCol(func(_ *sql.Ctx, options ...int) (string, error) { - return fmt.Sprintf("intDiv(timestamp_ns, %d) * %d", ctx.Step.Nanoseconds(), ctx.Step.Milliseconds()), nil - }) - return sql.NewSelect().With(withFpReq).Select( - sql.NewSimpleCol("fingerprint", "fingerprint"), - sql.NewCol(tsNsCol, "timestamp_ms"), - sql.NewCol(m.ValueCol, "value"), - sql.NewSimpleCol("1::UInt8", "original")). - From(sql.NewSimpleCol(ctx.MetricsTable, "metrics")). - AndWhere( - sql.Ge(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(ctx.From.UnixNano())), - sql.Le(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(ctx.To.UnixNano())), - sql.NewIn(sql.NewRawObject("fingerprint"), sql.NewWithRef(withFpReq))). - GroupBy(sql.NewRawObject("fingerprint"), sql.NewRawObject("timestamp_ms")), nil -} diff --git a/wasm_parts/promql/planners/metrics_zerofill.go b/wasm_parts/promql/planners/metrics_zerofill.go deleted file mode 100644 index 4f8fc703..00000000 --- a/wasm_parts/promql/planners/metrics_zerofill.go +++ /dev/null @@ -1,50 +0,0 @@ -package planners - -import ( - "fmt" - "wasm_parts/promql/shared" - sql "wasm_parts/sql_select" -) - -type MetricsZeroFillPlanner struct { - Main shared.RequestPlanner -} - -func (m *MetricsZeroFillPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { - main, err := m.Main.Process(ctx) - if err != nil { - return nil, err - } - main.OrderBy(sql.NewRawObject("fingerprint"), sql.NewCustomCol(func(_ *sql.Ctx, options ...int) (string, error) { - return fmt.Sprintf("timestamp_ms WITH FILL FROM %d TO %d STEP %d", - ctx.From.UnixMilli(), ctx.To.UnixMilli(), ctx.Step.Milliseconds()), nil - })) - return main, nil - /*withMain := sql.NewWith(main, "prezerofill") - arrLen := (ctx.To.UnixNano()-ctx.From.UnixNano())/ctx.Step.Nanoseconds() + 1 - zeroFillCol := sql.NewCustomCol(func(_ *sql.Ctx, options ...int) (string, error) { - return fmt.Sprintf("groupArrayInsertAt(nan, %d)(value, toUInt32(intDiv(timestamp_ms - %d, %d)))", - arrLen, ctx.From.UnixMilli(), ctx.Step.Milliseconds()), nil - }) - zeroFill := sql.NewSelect().With(withMain). - Select( - sql.NewSimpleCol("fingerprint", "fingerprint"), - sql.NewCol(zeroFillCol, "values")). - From(sql.NewWithRef(withMain)). - GroupBy(sql.NewRawObject("fingerprint")) - withZeroFill := sql.NewWith(zeroFill, "zerofill") - - joinZeroFillStmt := sql.NewCustomCol(func(_ *sql.Ctx, options ...int) (string, error) { - return fmt.Sprintf("arrayMap((x,y) -> (y * %d + %d, x), values, range(%d))", - ctx.Step.Milliseconds(), ctx.From.UnixMilli(), arrLen), nil - }) - - postZeroFill := sql.NewSelect().With(withZeroFill). - Select( - sql.NewSimpleCol("fingerprint", "fingerprint"), - sql.NewSimpleCol("timestamp_ms", "timestamp_ms"), - sql.NewSimpleCol("val.2", "value")). - From(sql.NewWithRef(withMain)). - Join(sql.NewJoin("array", sql.NewCol(joinZeroFillStmt, "val"), nil)) - return postZeroFill, nil*/ -} diff --git a/wasm_parts/promql/planners/stream_select_planner.go b/wasm_parts/promql/planners/stream_select_planner.go deleted file mode 100644 index af095b16..00000000 --- a/wasm_parts/promql/planners/stream_select_planner.go +++ /dev/null @@ -1,102 +0,0 @@ -package planners - -import ( - "fmt" - "github.com/prometheus/prometheus/model/labels" - "strings" - "wasm_parts/promql/shared" - sql "wasm_parts/sql_select" -) - -type StreamSelectPlanner struct { - Main shared.RequestPlanner - Matchers []*labels.Matcher -} - -func (s *StreamSelectPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { - main, err := s.Main.Process(ctx) - if err != nil { - return nil, err - } - conds := make([]sql.SQLCondition, len(s.Matchers)) - for i, m := range s.Matchers { - conds[i], err = s.getCond(m) - if err != nil { - return nil, err - } - } - main.AndWhere(sql.Or(conds...)) - - bitSetEntries := make([]*bitSetEntry, len(conds)) - for i, c := range conds { - bitSetEntries[i] = &bitSetEntry{c, i} - } - main.AndHaving(sql.Eq(&bitSet{entries: bitSetEntries}, sql.NewIntVal((int64(1)<<uint(len(conds)))-1))) - return main, nil -} - -func (s *StreamSelectPlanner) getCond(m *labels.Matcher) (sql.SQLCondition, error) { - keyCond := sql.Eq(sql.NewRawObject("key"), sql.NewStringVal(m.Name)) - var valCond sql.SQLCondition - switch m.Type { - case labels.MatchEqual: - valCond = sql.Eq(sql.NewRawObject("val"), sql.NewStringVal(m.Value)) - case labels.MatchNotEqual: - valCond = sql.Neq(sql.NewRawObject("val"), sql.NewStringVal(m.Value)) - case labels.MatchRegexp: - valCond = sql.Eq(&pregMatch{sql.NewRawObject("val"), sql.NewStringVal(m.Value)}, - sql.NewIntVal(1)) - case labels.MatchNotRegexp: - valCond = sql.Eq(&pregMatch{sql.NewRawObject("val"), sql.NewStringVal(m.Value)}, - sql.NewIntVal(0)) - default: - return nil, fmt.Errorf("unknown matcher type: %v", m.Type) - } - return sql.And(keyCond, valCond), nil -} - -type pregMatch struct { - key sql.SQLObject - val sql.SQLObject -} - -func (p *pregMatch) String(ctx *sql.Ctx, options ...int) (string, error) { - strK, err := p.key.String(ctx, options...) - if err != nil { - return "", err - } - strV, err := p.val.String(ctx, options...) - if err != nil { - return "", err - } - return fmt.Sprintf("match(%s, %s)", strK, strV), nil -} - -type bitSetEntry struct { - cond sql.SQLCondition - idx int -} - -func (b bitSetEntry) String(ctx *sql.Ctx, options ...int) (string, error) { - strCond, err := b.cond.String(ctx, options...) - if err != nil { - return "", err - } - return fmt.Sprintf("bitShiftLeft(toUInt64(%s), %d)", strCond, b.idx), nil -} - -type bitSet struct { - entries []*bitSetEntry -} - -func (b bitSet) String(ctx *sql.Ctx, options ...int) (string, error) { - strEntries := make([]string, len(b.entries)) - var err error - for i, e := range b.entries { - strEntries[i], err = e.String(ctx, options...) - if err != nil { - return "", err - } - } - return fmt.Sprintf("groupBitOr(%s)", strings.Join(strEntries, "+")), nil -} diff --git a/wasm_parts/promql/planners/time_series_gin_init.go b/wasm_parts/promql/planners/time_series_gin_init.go deleted file mode 100644 index 01aa7d04..00000000 --- a/wasm_parts/promql/planners/time_series_gin_init.go +++ /dev/null @@ -1,20 +0,0 @@ -package planners - -import ( - "wasm_parts/promql/shared" - sql "wasm_parts/sql_select" -) - -type TimeSeriesGinInitPlanner struct { -} - -func (t *TimeSeriesGinInitPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { - return sql.NewSelect(). - Select(sql.NewSimpleCol("fingerprint", "fingerprint")). - From(sql.NewSimpleCol(ctx.TimeSeriesGinTable, "ts_gin")). - AndWhere( - sql.Ge(sql.NewRawObject("date"), sql.NewStringVal(ctx.From.Format("2006-01-02"))), - sql.Le(sql.NewRawObject("date"), sql.NewStringVal(ctx.To.Format("2006-01-02"))), - sql.NewIn(sql.NewRawObject("type"), sql.NewIntVal(0), sql.NewIntVal(2))). - GroupBy(sql.NewRawObject("fingerprint")), nil -} diff --git a/wasm_parts/promql/rate.go b/wasm_parts/promql/rate.go deleted file mode 100644 index 1312fb91..00000000 --- a/wasm_parts/promql/rate.go +++ /dev/null @@ -1,62 +0,0 @@ -package promql - -import ( - "github.com/prometheus/prometheus/promql/parser" - "wasm_parts/promql/planners" - "wasm_parts/promql/shared" -) - -type RateOptimizer struct { - vectorSelectorOptimizer *VectorSelectorOptimizer -} - -func (r *RateOptimizer) IsAppliable(node parser.Node) bool { - _node, ok := node.(*parser.Call) - if !ok { - return false - } - vectorSelector := r.getVectorSelector(_node) - return vectorSelector != nil && (&VectorSelectorOptimizer{}).IsAppliable(vectorSelector) -} - -func (r *RateOptimizer) Optimize(node parser.Node) (shared.RequestPlanner, error) { - _node, ok := node.(*parser.Call) - if !ok { - return nil, nil - } - vectorSelector := r.getVectorSelector(_node) - matrixSelector := _node.Args[0].(*parser.MatrixSelector) - res, err := (&VectorSelectorOptimizer{}).Optimize(vectorSelector) - if err != nil { - return nil, err - } - res = &planners.RatePlanner{ - Main: res, - Duration: matrixSelector.Range, - } - return res, nil -} - -func (v *RateOptimizer) PlanOptimize(node parser.Node) error { - v.vectorSelectorOptimizer = &VectorSelectorOptimizer{} - return v.vectorSelectorOptimizer.PlanOptimize(node) -} - -func (r *RateOptimizer) getVectorSelector(node *parser.Call) *parser.VectorSelector { - if node.Func.Name != "rate" || len(node.Args) != 1 { - return nil - } - _matrixSelector, ok := node.Args[0].(*parser.MatrixSelector) - if !ok { - return nil - } - vectorSelector, ok := _matrixSelector.VectorSelector.(*parser.VectorSelector) - if !ok { - return nil - } - return vectorSelector -} - -func (r *RateOptimizer) Children() []IOptimizer { - return []IOptimizer{r.vectorSelectorOptimizer} -} diff --git a/wasm_parts/promql/shared/types.go b/wasm_parts/promql/shared/types.go deleted file mode 100644 index 805a408a..00000000 --- a/wasm_parts/promql/shared/types.go +++ /dev/null @@ -1,22 +0,0 @@ -package shared - -import ( - "time" - sql "wasm_parts/sql_select" -) - -type RequestPlanner interface { - Process(ctx *PlannerContext) (sql.ISelect, error) -} - -type PlannerContext struct { - IsCluster bool - From time.Time - To time.Time - Step time.Duration - TimeSeriesTable string - TimeSeriesDistTable string - TimeSeriesGinTable string - MetricsTable string - MetricsDistTable string -} diff --git a/wasm_parts/promql/smart_optimizers.go b/wasm_parts/promql/smart_optimizers.go deleted file mode 100644 index c93cdb8f..00000000 --- a/wasm_parts/promql/smart_optimizers.go +++ /dev/null @@ -1,45 +0,0 @@ -package promql - -import ( - "github.com/prometheus/prometheus/promql/parser" - "wasm_parts/promql/shared" -) - -type IOptimizer interface { - IsAppliable(node parser.Node) bool - Optimize(node parser.Node) (shared.RequestPlanner, error) - PlanOptimize(node parser.Node) error - Children() []IOptimizer -} - -type OptimizerFactory func() IOptimizer - -var VectorSelectorOptimizerFactory OptimizerFactory = func() IOptimizer { - return &VectorSelectorOptimizer{} -} - -var FinalizerOptimizerFactory OptimizerFactory = func() IOptimizer { - return &FinalizerOptimizer{} -} - -var Optimizers = []OptimizerFactory{ - func() IOptimizer { - return &RateOptimizer{} - }, - func() IOptimizer { - return &AggregateOptimizer{} - }, -} - -func GetAppliableOptimizer(node parser.Node, factories []OptimizerFactory) IOptimizer { - if factories == nil { - factories = Optimizers - } - for _, factory := range factories { - opt := factory() - if opt.IsAppliable(node) { - return opt - } - } - return nil -} diff --git a/wasm_parts/promql/vector.go b/wasm_parts/promql/vector.go deleted file mode 100644 index a7dc1c87..00000000 --- a/wasm_parts/promql/vector.go +++ /dev/null @@ -1,39 +0,0 @@ -package promql - -import ( - "github.com/prometheus/prometheus/promql/parser" - "wasm_parts/promql/planners" - "wasm_parts/promql/shared" -) - -type VectorSelectorOptimizer struct { -} - -func (v *VectorSelectorOptimizer) IsAppliable(node parser.Node) bool { - _, ok := node.(*parser.VectorSelector) - return ok -} - -func (v *VectorSelectorOptimizer) PlanOptimize(node parser.Node) error { - return nil -} - -func (v *VectorSelectorOptimizer) Optimize(node parser.Node) (shared.RequestPlanner, error) { - _node := node.(*parser.VectorSelector) - var res shared.RequestPlanner = &planners.TimeSeriesGinInitPlanner{} - res = &planners.StreamSelectPlanner{ - Main: res, - Matchers: _node.LabelMatchers, - } - res = &planners.MetricsInitPlanner{ - ValueCol: nil, - Fingerprint: res, - } - res = &planners.MetricsZeroFillPlanner{Main: res} - res = &planners.MetricsExtendPlanner{Main: res} - return res, nil -} - -func (v *VectorSelectorOptimizer) Children() []IOptimizer { - return nil -} diff --git a/wasm_parts/traceql/shared/errors.go b/wasm_parts/traceql/shared/errors.go deleted file mode 100644 index caf0e155..00000000 --- a/wasm_parts/traceql/shared/errors.go +++ /dev/null @@ -1,14 +0,0 @@ -package shared - -type NotSupportedError struct { - Msg string -} - -func (n *NotSupportedError) Error() string { - return n.Msg -} - -func isNotSupportedError(e error) bool { - _, ok := e.(*NotSupportedError) - return ok -} diff --git a/wasm_parts/traceql/shared/plannerCtx.go b/wasm_parts/traceql/shared/plannerCtx.go deleted file mode 100644 index 69d49885..00000000 --- a/wasm_parts/traceql/shared/plannerCtx.go +++ /dev/null @@ -1,51 +0,0 @@ -package shared - -import ( - "context" - "time" - sql "wasm_parts/sql_select" -) - -type PlannerContext struct { - IsCluster bool - OrgID string - From time.Time - To time.Time - FromS int32 - ToS int32 - OrderASC bool - Limit int64 - - TimeSeriesGinTableName string - SamplesTableName string - TimeSeriesTableName string - TimeSeriesDistTableName string - Metrics15sTableName string - - TracesAttrsTable string - TracesAttrsDistTable string - TracesTable string - TracesDistTable string - - UseCache bool - - Ctx context.Context - CancelCtx context.CancelFunc - - CHFinalize bool - CHSqlCtx *sql.Ctx - - DDBSamplesTable string - DDBTSTable string - - Step time.Duration - - DeleteID string - - id int -} - -func (p *PlannerContext) Id() int { - p.id++ - return p.id -} diff --git a/wasm_parts/traceql/shared/plannerCtx_ffjson.go b/wasm_parts/traceql/shared/plannerCtx_ffjson.go deleted file mode 100644 index 0c2c928f..00000000 --- a/wasm_parts/traceql/shared/plannerCtx_ffjson.go +++ /dev/null @@ -1,1462 +0,0 @@ -// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT. -// source: plannerCtx.go - -package shared - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - fflib "github.com/pquerna/ffjson/fflib/v1" - "time" -) - -// MarshalJSON marshal bytes to json - template -func (j *PlannerContext) MarshalJSON() ([]byte, error) { - var buf fflib.Buffer - if j == nil { - buf.WriteString("null") - return buf.Bytes(), nil - } - err := j.MarshalJSONBuf(&buf) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// MarshalJSONBuf marshal buff to json - template -func (j *PlannerContext) MarshalJSONBuf(buf fflib.EncodingBuffer) error { - if j == nil { - buf.WriteString("null") - return nil - } - var err error - var obj []byte - _ = obj - _ = err - if j.IsCluster { - buf.WriteString(`{"IsCluster":true`) - } else { - buf.WriteString(`{"IsCluster":false`) - } - buf.WriteString(`,"OrgID":`) - fflib.WriteJsonString(buf, string(j.OrgID)) - buf.WriteString(`,"From":`) - - { - - obj, err = j.From.MarshalJSON() - if err != nil { - return err - } - buf.Write(obj) - - } - buf.WriteString(`,"To":`) - - { - - obj, err = j.To.MarshalJSON() - if err != nil { - return err - } - buf.Write(obj) - - } - buf.WriteString(`,"FromS":`) - fflib.FormatBits2(buf, uint64(j.FromS), 10, j.FromS < 0) - buf.WriteString(`,"ToS":`) - fflib.FormatBits2(buf, uint64(j.ToS), 10, j.ToS < 0) - if j.OrderASC { - buf.WriteString(`,"OrderASC":true`) - } else { - buf.WriteString(`,"OrderASC":false`) - } - buf.WriteString(`,"Limit":`) - fflib.FormatBits2(buf, uint64(j.Limit), 10, j.Limit < 0) - buf.WriteString(`,"TimeSeriesGinTableName":`) - fflib.WriteJsonString(buf, string(j.TimeSeriesGinTableName)) - buf.WriteString(`,"SamplesTableName":`) - fflib.WriteJsonString(buf, string(j.SamplesTableName)) - buf.WriteString(`,"TimeSeriesTableName":`) - fflib.WriteJsonString(buf, string(j.TimeSeriesTableName)) - buf.WriteString(`,"TimeSeriesDistTableName":`) - fflib.WriteJsonString(buf, string(j.TimeSeriesDistTableName)) - buf.WriteString(`,"Metrics15sTableName":`) - fflib.WriteJsonString(buf, string(j.Metrics15sTableName)) - buf.WriteString(`,"TracesAttrsTable":`) - fflib.WriteJsonString(buf, string(j.TracesAttrsTable)) - buf.WriteString(`,"TracesAttrsDistTable":`) - fflib.WriteJsonString(buf, string(j.TracesAttrsDistTable)) - buf.WriteString(`,"TracesTable":`) - fflib.WriteJsonString(buf, string(j.TracesTable)) - buf.WriteString(`,"TracesDistTable":`) - fflib.WriteJsonString(buf, string(j.TracesDistTable)) - if j.UseCache { - buf.WriteString(`,"UseCache":true`) - } else { - buf.WriteString(`,"UseCache":false`) - } - buf.WriteString(`,"Ctx":`) - /* Interface types must use runtime reflection. type=context.Context kind=interface */ - err = buf.Encode(j.Ctx) - if err != nil { - return err - } - buf.WriteString(`,"CancelCtx":`) - /* Falling back. type=context.CancelFunc kind=func */ - err = buf.Encode(j.CancelCtx) - if err != nil { - return err - } - if j.CHFinalize { - buf.WriteString(`,"CHFinalize":true`) - } else { - buf.WriteString(`,"CHFinalize":false`) - } - if j.CHSqlCtx != nil { - /* Struct fall back. type=sql.Ctx kind=struct */ - buf.WriteString(`,"CHSqlCtx":`) - err = buf.Encode(j.CHSqlCtx) - if err != nil { - return err - } - } else { - buf.WriteString(`,"CHSqlCtx":null`) - } - buf.WriteString(`,"DDBSamplesTable":`) - fflib.WriteJsonString(buf, string(j.DDBSamplesTable)) - buf.WriteString(`,"DDBTSTable":`) - fflib.WriteJsonString(buf, string(j.DDBTSTable)) - buf.WriteString(`,"Step":`) - fflib.FormatBits2(buf, uint64(j.Step), 10, j.Step < 0) - buf.WriteString(`,"DeleteID":`) - fflib.WriteJsonString(buf, string(j.DeleteID)) - buf.WriteByte('}') - return nil -} - -const ( - ffjtPlannerContextbase = iota - ffjtPlannerContextnosuchkey - - ffjtPlannerContextIsCluster - - ffjtPlannerContextOrgID - - ffjtPlannerContextFrom - - ffjtPlannerContextTo - - ffjtPlannerContextFromS - - ffjtPlannerContextToS - - ffjtPlannerContextOrderASC - - ffjtPlannerContextLimit - - ffjtPlannerContextTimeSeriesGinTableName - - ffjtPlannerContextSamplesTableName - - ffjtPlannerContextTimeSeriesTableName - - ffjtPlannerContextTimeSeriesDistTableName - - ffjtPlannerContextMetrics15sTableName - - ffjtPlannerContextTracesAttrsTable - - ffjtPlannerContextTracesAttrsDistTable - - ffjtPlannerContextTracesTable - - ffjtPlannerContextTracesDistTable - - ffjtPlannerContextUseCache - - ffjtPlannerContextCtx - - ffjtPlannerContextCancelCtx - - ffjtPlannerContextCHFinalize - - ffjtPlannerContextCHSqlCtx - - ffjtPlannerContextDDBSamplesTable - - ffjtPlannerContextDDBTSTable - - ffjtPlannerContextStep - - ffjtPlannerContextDeleteID -) - -var ffjKeyPlannerContextIsCluster = []byte("IsCluster") - -var ffjKeyPlannerContextOrgID = []byte("OrgID") - -var ffjKeyPlannerContextFrom = []byte("From") - -var ffjKeyPlannerContextTo = []byte("To") - -var ffjKeyPlannerContextFromS = []byte("FromS") - -var ffjKeyPlannerContextToS = []byte("ToS") - -var ffjKeyPlannerContextOrderASC = []byte("OrderASC") - -var ffjKeyPlannerContextLimit = []byte("Limit") - -var ffjKeyPlannerContextTimeSeriesGinTableName = []byte("TimeSeriesGinTableName") - -var ffjKeyPlannerContextSamplesTableName = []byte("SamplesTableName") - -var ffjKeyPlannerContextTimeSeriesTableName = []byte("TimeSeriesTableName") - -var ffjKeyPlannerContextTimeSeriesDistTableName = []byte("TimeSeriesDistTableName") - -var ffjKeyPlannerContextMetrics15sTableName = []byte("Metrics15sTableName") - -var ffjKeyPlannerContextTracesAttrsTable = []byte("TracesAttrsTable") - -var ffjKeyPlannerContextTracesAttrsDistTable = []byte("TracesAttrsDistTable") - -var ffjKeyPlannerContextTracesTable = []byte("TracesTable") - -var ffjKeyPlannerContextTracesDistTable = []byte("TracesDistTable") - -var ffjKeyPlannerContextUseCache = []byte("UseCache") - -var ffjKeyPlannerContextCtx = []byte("Ctx") - -var ffjKeyPlannerContextCancelCtx = []byte("CancelCtx") - -var ffjKeyPlannerContextCHFinalize = []byte("CHFinalize") - -var ffjKeyPlannerContextCHSqlCtx = []byte("CHSqlCtx") - -var ffjKeyPlannerContextDDBSamplesTable = []byte("DDBSamplesTable") - -var ffjKeyPlannerContextDDBTSTable = []byte("DDBTSTable") - -var ffjKeyPlannerContextStep = []byte("Step") - -var ffjKeyPlannerContextDeleteID = []byte("DeleteID") - -// UnmarshalJSON umarshall json - template of ffjson -func (j *PlannerContext) UnmarshalJSON(input []byte) error { - fs := fflib.NewFFLexer(input) - return j.UnmarshalJSONFFLexer(fs, fflib.FFParse_map_start) -} - -// UnmarshalJSONFFLexer fast json unmarshall - template ffjson -func (j *PlannerContext) UnmarshalJSONFFLexer(fs *fflib.FFLexer, state fflib.FFParseState) error { - var err error - currentKey := ffjtPlannerContextbase - _ = currentKey - tok := fflib.FFTok_init - wantedTok := fflib.FFTok_init - -mainparse: - for { - tok = fs.Scan() - // println(fmt.Sprintf("debug: tok: %v state: %v", tok, state)) - if tok == fflib.FFTok_error { - goto tokerror - } - - switch state { - - case fflib.FFParse_map_start: - if tok != fflib.FFTok_left_bracket { - wantedTok = fflib.FFTok_left_bracket - goto wrongtokenerror - } - state = fflib.FFParse_want_key - continue - - case fflib.FFParse_after_value: - if tok == fflib.FFTok_comma { - state = fflib.FFParse_want_key - } else if tok == fflib.FFTok_right_bracket { - goto done - } else { - wantedTok = fflib.FFTok_comma - goto wrongtokenerror - } - - case fflib.FFParse_want_key: - // json {} ended. goto exit. woo. - if tok == fflib.FFTok_right_bracket { - goto done - } - if tok != fflib.FFTok_string { - wantedTok = fflib.FFTok_string - goto wrongtokenerror - } - - kn := fs.Output.Bytes() - if len(kn) <= 0 { - // "" case. hrm. - currentKey = ffjtPlannerContextnosuchkey - state = fflib.FFParse_want_colon - goto mainparse - } else { - switch kn[0] { - - case 'C': - - if bytes.Equal(ffjKeyPlannerContextCtx, kn) { - currentKey = ffjtPlannerContextCtx - state = fflib.FFParse_want_colon - goto mainparse - - } else if bytes.Equal(ffjKeyPlannerContextCancelCtx, kn) { - currentKey = ffjtPlannerContextCancelCtx - state = fflib.FFParse_want_colon - goto mainparse - - } else if bytes.Equal(ffjKeyPlannerContextCHFinalize, kn) { - currentKey = ffjtPlannerContextCHFinalize - state = fflib.FFParse_want_colon - goto mainparse - - } else if bytes.Equal(ffjKeyPlannerContextCHSqlCtx, kn) { - currentKey = ffjtPlannerContextCHSqlCtx - state = fflib.FFParse_want_colon - goto mainparse - } - - case 'D': - - if bytes.Equal(ffjKeyPlannerContextDDBSamplesTable, kn) { - currentKey = ffjtPlannerContextDDBSamplesTable - state = fflib.FFParse_want_colon - goto mainparse - - } else if bytes.Equal(ffjKeyPlannerContextDDBTSTable, kn) { - currentKey = ffjtPlannerContextDDBTSTable - state = fflib.FFParse_want_colon - goto mainparse - - } else if bytes.Equal(ffjKeyPlannerContextDeleteID, kn) { - currentKey = ffjtPlannerContextDeleteID - state = fflib.FFParse_want_colon - goto mainparse - } - - case 'F': - - if bytes.Equal(ffjKeyPlannerContextFrom, kn) { - currentKey = ffjtPlannerContextFrom - state = fflib.FFParse_want_colon - goto mainparse - - } else if bytes.Equal(ffjKeyPlannerContextFromS, kn) { - currentKey = ffjtPlannerContextFromS - state = fflib.FFParse_want_colon - goto mainparse - } - - case 'I': - - if bytes.Equal(ffjKeyPlannerContextIsCluster, kn) { - currentKey = ffjtPlannerContextIsCluster - state = fflib.FFParse_want_colon - goto mainparse - } - - case 'L': - - if bytes.Equal(ffjKeyPlannerContextLimit, kn) { - currentKey = ffjtPlannerContextLimit - state = fflib.FFParse_want_colon - goto mainparse - } - - case 'M': - - if bytes.Equal(ffjKeyPlannerContextMetrics15sTableName, kn) { - currentKey = ffjtPlannerContextMetrics15sTableName - state = fflib.FFParse_want_colon - goto mainparse - } - - case 'O': - - if bytes.Equal(ffjKeyPlannerContextOrgID, kn) { - currentKey = ffjtPlannerContextOrgID - state = fflib.FFParse_want_colon - goto mainparse - - } else if bytes.Equal(ffjKeyPlannerContextOrderASC, kn) { - currentKey = ffjtPlannerContextOrderASC - state = fflib.FFParse_want_colon - goto mainparse - } - - case 'S': - - if bytes.Equal(ffjKeyPlannerContextSamplesTableName, kn) { - currentKey = ffjtPlannerContextSamplesTableName - state = fflib.FFParse_want_colon - goto mainparse - - } else if bytes.Equal(ffjKeyPlannerContextStep, kn) { - currentKey = ffjtPlannerContextStep - state = fflib.FFParse_want_colon - goto mainparse - } - - case 'T': - - if bytes.Equal(ffjKeyPlannerContextTo, kn) { - currentKey = ffjtPlannerContextTo - state = fflib.FFParse_want_colon - goto mainparse - - } else if bytes.Equal(ffjKeyPlannerContextToS, kn) { - currentKey = ffjtPlannerContextToS - state = fflib.FFParse_want_colon - goto mainparse - - } else if bytes.Equal(ffjKeyPlannerContextTimeSeriesGinTableName, kn) { - currentKey = ffjtPlannerContextTimeSeriesGinTableName - state = fflib.FFParse_want_colon - goto mainparse - - } else if bytes.Equal(ffjKeyPlannerContextTimeSeriesTableName, kn) { - currentKey = ffjtPlannerContextTimeSeriesTableName - state = fflib.FFParse_want_colon - goto mainparse - - } else if bytes.Equal(ffjKeyPlannerContextTimeSeriesDistTableName, kn) { - currentKey = ffjtPlannerContextTimeSeriesDistTableName - state = fflib.FFParse_want_colon - goto mainparse - - } else if bytes.Equal(ffjKeyPlannerContextTracesAttrsTable, kn) { - currentKey = ffjtPlannerContextTracesAttrsTable - state = fflib.FFParse_want_colon - goto mainparse - - } else if bytes.Equal(ffjKeyPlannerContextTracesAttrsDistTable, kn) { - currentKey = ffjtPlannerContextTracesAttrsDistTable - state = fflib.FFParse_want_colon - goto mainparse - - } else if bytes.Equal(ffjKeyPlannerContextTracesTable, kn) { - currentKey = ffjtPlannerContextTracesTable - state = fflib.FFParse_want_colon - goto mainparse - - } else if bytes.Equal(ffjKeyPlannerContextTracesDistTable, kn) { - currentKey = ffjtPlannerContextTracesDistTable - state = fflib.FFParse_want_colon - goto mainparse - } - - case 'U': - - if bytes.Equal(ffjKeyPlannerContextUseCache, kn) { - currentKey = ffjtPlannerContextUseCache - state = fflib.FFParse_want_colon - goto mainparse - } - - } - - if fflib.SimpleLetterEqualFold(ffjKeyPlannerContextDeleteID, kn) { - currentKey = ffjtPlannerContextDeleteID - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextStep, kn) { - currentKey = ffjtPlannerContextStep - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextDDBTSTable, kn) { - currentKey = ffjtPlannerContextDDBTSTable - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextDDBSamplesTable, kn) { - currentKey = ffjtPlannerContextDDBSamplesTable - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextCHSqlCtx, kn) { - currentKey = ffjtPlannerContextCHSqlCtx - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.SimpleLetterEqualFold(ffjKeyPlannerContextCHFinalize, kn) { - currentKey = ffjtPlannerContextCHFinalize - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.SimpleLetterEqualFold(ffjKeyPlannerContextCancelCtx, kn) { - currentKey = ffjtPlannerContextCancelCtx - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.SimpleLetterEqualFold(ffjKeyPlannerContextCtx, kn) { - currentKey = ffjtPlannerContextCtx - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextUseCache, kn) { - currentKey = ffjtPlannerContextUseCache - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextTracesDistTable, kn) { - currentKey = ffjtPlannerContextTracesDistTable - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextTracesTable, kn) { - currentKey = ffjtPlannerContextTracesTable - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextTracesAttrsDistTable, kn) { - currentKey = ffjtPlannerContextTracesAttrsDistTable - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextTracesAttrsTable, kn) { - currentKey = ffjtPlannerContextTracesAttrsTable - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextMetrics15sTableName, kn) { - currentKey = ffjtPlannerContextMetrics15sTableName - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextTimeSeriesDistTableName, kn) { - currentKey = ffjtPlannerContextTimeSeriesDistTableName - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextTimeSeriesTableName, kn) { - currentKey = ffjtPlannerContextTimeSeriesTableName - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextSamplesTableName, kn) { - currentKey = ffjtPlannerContextSamplesTableName - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextTimeSeriesGinTableName, kn) { - currentKey = ffjtPlannerContextTimeSeriesGinTableName - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.SimpleLetterEqualFold(ffjKeyPlannerContextLimit, kn) { - currentKey = ffjtPlannerContextLimit - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextOrderASC, kn) { - currentKey = ffjtPlannerContextOrderASC - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextToS, kn) { - currentKey = ffjtPlannerContextToS - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextFromS, kn) { - currentKey = ffjtPlannerContextFromS - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.SimpleLetterEqualFold(ffjKeyPlannerContextTo, kn) { - currentKey = ffjtPlannerContextTo - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.SimpleLetterEqualFold(ffjKeyPlannerContextFrom, kn) { - currentKey = ffjtPlannerContextFrom - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.SimpleLetterEqualFold(ffjKeyPlannerContextOrgID, kn) { - currentKey = ffjtPlannerContextOrgID - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyPlannerContextIsCluster, kn) { - currentKey = ffjtPlannerContextIsCluster - state = fflib.FFParse_want_colon - goto mainparse - } - - currentKey = ffjtPlannerContextnosuchkey - state = fflib.FFParse_want_colon - goto mainparse - } - - case fflib.FFParse_want_colon: - if tok != fflib.FFTok_colon { - wantedTok = fflib.FFTok_colon - goto wrongtokenerror - } - state = fflib.FFParse_want_value - continue - case fflib.FFParse_want_value: - - if tok == fflib.FFTok_left_brace || tok == fflib.FFTok_left_bracket || tok == fflib.FFTok_integer || tok == fflib.FFTok_double || tok == fflib.FFTok_string || tok == fflib.FFTok_bool || tok == fflib.FFTok_null { - switch currentKey { - - case ffjtPlannerContextIsCluster: - goto handle_IsCluster - - case ffjtPlannerContextOrgID: - goto handle_OrgID - - case ffjtPlannerContextFrom: - goto handle_From - - case ffjtPlannerContextTo: - goto handle_To - - case ffjtPlannerContextFromS: - goto handle_FromS - - case ffjtPlannerContextToS: - goto handle_ToS - - case ffjtPlannerContextOrderASC: - goto handle_OrderASC - - case ffjtPlannerContextLimit: - goto handle_Limit - - case ffjtPlannerContextTimeSeriesGinTableName: - goto handle_TimeSeriesGinTableName - - case ffjtPlannerContextSamplesTableName: - goto handle_SamplesTableName - - case ffjtPlannerContextTimeSeriesTableName: - goto handle_TimeSeriesTableName - - case ffjtPlannerContextTimeSeriesDistTableName: - goto handle_TimeSeriesDistTableName - - case ffjtPlannerContextMetrics15sTableName: - goto handle_Metrics15sTableName - - case ffjtPlannerContextTracesAttrsTable: - goto handle_TracesAttrsTable - - case ffjtPlannerContextTracesAttrsDistTable: - goto handle_TracesAttrsDistTable - - case ffjtPlannerContextTracesTable: - goto handle_TracesTable - - case ffjtPlannerContextTracesDistTable: - goto handle_TracesDistTable - - case ffjtPlannerContextUseCache: - goto handle_UseCache - - case ffjtPlannerContextCtx: - goto handle_Ctx - - case ffjtPlannerContextCancelCtx: - goto handle_CancelCtx - - case ffjtPlannerContextCHFinalize: - goto handle_CHFinalize - - case ffjtPlannerContextCHSqlCtx: - goto handle_CHSqlCtx - - case ffjtPlannerContextDDBSamplesTable: - goto handle_DDBSamplesTable - - case ffjtPlannerContextDDBTSTable: - goto handle_DDBTSTable - - case ffjtPlannerContextStep: - goto handle_Step - - case ffjtPlannerContextDeleteID: - goto handle_DeleteID - - case ffjtPlannerContextnosuchkey: - err = fs.SkipField(tok) - if err != nil { - return fs.WrapErr(err) - } - state = fflib.FFParse_after_value - goto mainparse - } - } else { - goto wantedvalue - } - } - } - -handle_IsCluster: - - /* handler: j.IsCluster type=bool kind=bool quoted=false*/ - - { - if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) - } - } - - { - if tok == fflib.FFTok_null { - - } else { - tmpb := fs.Output.Bytes() - - if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { - - j.IsCluster = true - - } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { - - j.IsCluster = false - - } else { - err = errors.New("unexpected bytes for true/false value") - return fs.WrapErr(err) - } - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_OrgID: - - /* handler: j.OrgID type=string kind=string quoted=false*/ - - { - - { - if tok != fflib.FFTok_string && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) - } - } - - if tok == fflib.FFTok_null { - - } else { - - outBuf := fs.Output.Bytes() - - j.OrgID = string(string(outBuf)) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_From: - - /* handler: j.From type=time.Time kind=struct quoted=false*/ - - { - if tok == fflib.FFTok_null { - - } else { - - tbuf, err := fs.CaptureField(tok) - if err != nil { - return fs.WrapErr(err) - } - - err = j.From.UnmarshalJSON(tbuf) - if err != nil { - return fs.WrapErr(err) - } - } - state = fflib.FFParse_after_value - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_To: - - /* handler: j.To type=time.Time kind=struct quoted=false*/ - - { - if tok == fflib.FFTok_null { - - } else { - - tbuf, err := fs.CaptureField(tok) - if err != nil { - return fs.WrapErr(err) - } - - err = j.To.UnmarshalJSON(tbuf) - if err != nil { - return fs.WrapErr(err) - } - } - state = fflib.FFParse_after_value - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_FromS: - - /* handler: j.FromS type=int32 kind=int32 quoted=false*/ - - { - if tok != fflib.FFTok_integer && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for int32", tok)) - } - } - - { - - if tok == fflib.FFTok_null { - - } else { - - tval, err := fflib.ParseInt(fs.Output.Bytes(), 10, 32) - - if err != nil { - return fs.WrapErr(err) - } - - j.FromS = int32(tval) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_ToS: - - /* handler: j.ToS type=int32 kind=int32 quoted=false*/ - - { - if tok != fflib.FFTok_integer && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for int32", tok)) - } - } - - { - - if tok == fflib.FFTok_null { - - } else { - - tval, err := fflib.ParseInt(fs.Output.Bytes(), 10, 32) - - if err != nil { - return fs.WrapErr(err) - } - - j.ToS = int32(tval) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_OrderASC: - - /* handler: j.OrderASC type=bool kind=bool quoted=false*/ - - { - if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) - } - } - - { - if tok == fflib.FFTok_null { - - } else { - tmpb := fs.Output.Bytes() - - if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { - - j.OrderASC = true - - } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { - - j.OrderASC = false - - } else { - err = errors.New("unexpected bytes for true/false value") - return fs.WrapErr(err) - } - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_Limit: - - /* handler: j.Limit type=int64 kind=int64 quoted=false*/ - - { - if tok != fflib.FFTok_integer && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for int64", tok)) - } - } - - { - - if tok == fflib.FFTok_null { - - } else { - - tval, err := fflib.ParseInt(fs.Output.Bytes(), 10, 64) - - if err != nil { - return fs.WrapErr(err) - } - - j.Limit = int64(tval) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_TimeSeriesGinTableName: - - /* handler: j.TimeSeriesGinTableName type=string kind=string quoted=false*/ - - { - - { - if tok != fflib.FFTok_string && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) - } - } - - if tok == fflib.FFTok_null { - - } else { - - outBuf := fs.Output.Bytes() - - j.TimeSeriesGinTableName = string(string(outBuf)) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_SamplesTableName: - - /* handler: j.SamplesTableName type=string kind=string quoted=false*/ - - { - - { - if tok != fflib.FFTok_string && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) - } - } - - if tok == fflib.FFTok_null { - - } else { - - outBuf := fs.Output.Bytes() - - j.SamplesTableName = string(string(outBuf)) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_TimeSeriesTableName: - - /* handler: j.TimeSeriesTableName type=string kind=string quoted=false*/ - - { - - { - if tok != fflib.FFTok_string && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) - } - } - - if tok == fflib.FFTok_null { - - } else { - - outBuf := fs.Output.Bytes() - - j.TimeSeriesTableName = string(string(outBuf)) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_TimeSeriesDistTableName: - - /* handler: j.TimeSeriesDistTableName type=string kind=string quoted=false*/ - - { - - { - if tok != fflib.FFTok_string && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) - } - } - - if tok == fflib.FFTok_null { - - } else { - - outBuf := fs.Output.Bytes() - - j.TimeSeriesDistTableName = string(string(outBuf)) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_Metrics15sTableName: - - /* handler: j.Metrics15sTableName type=string kind=string quoted=false*/ - - { - - { - if tok != fflib.FFTok_string && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) - } - } - - if tok == fflib.FFTok_null { - - } else { - - outBuf := fs.Output.Bytes() - - j.Metrics15sTableName = string(string(outBuf)) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_TracesAttrsTable: - - /* handler: j.TracesAttrsTable type=string kind=string quoted=false*/ - - { - - { - if tok != fflib.FFTok_string && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) - } - } - - if tok == fflib.FFTok_null { - - } else { - - outBuf := fs.Output.Bytes() - - j.TracesAttrsTable = string(string(outBuf)) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_TracesAttrsDistTable: - - /* handler: j.TracesAttrsDistTable type=string kind=string quoted=false*/ - - { - - { - if tok != fflib.FFTok_string && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) - } - } - - if tok == fflib.FFTok_null { - - } else { - - outBuf := fs.Output.Bytes() - - j.TracesAttrsDistTable = string(string(outBuf)) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_TracesTable: - - /* handler: j.TracesTable type=string kind=string quoted=false*/ - - { - - { - if tok != fflib.FFTok_string && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) - } - } - - if tok == fflib.FFTok_null { - - } else { - - outBuf := fs.Output.Bytes() - - j.TracesTable = string(string(outBuf)) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_TracesDistTable: - - /* handler: j.TracesDistTable type=string kind=string quoted=false*/ - - { - - { - if tok != fflib.FFTok_string && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) - } - } - - if tok == fflib.FFTok_null { - - } else { - - outBuf := fs.Output.Bytes() - - j.TracesDistTable = string(string(outBuf)) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_UseCache: - - /* handler: j.UseCache type=bool kind=bool quoted=false*/ - - { - if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) - } - } - - { - if tok == fflib.FFTok_null { - - } else { - tmpb := fs.Output.Bytes() - - if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { - - j.UseCache = true - - } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { - - j.UseCache = false - - } else { - err = errors.New("unexpected bytes for true/false value") - return fs.WrapErr(err) - } - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_Ctx: - - /* handler: j.Ctx type=context.Context kind=interface quoted=false*/ - - { - /* Falling back. type=context.Context kind=interface */ - tbuf, err := fs.CaptureField(tok) - if err != nil { - return fs.WrapErr(err) - } - - err = json.Unmarshal(tbuf, &j.Ctx) - if err != nil { - return fs.WrapErr(err) - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_CancelCtx: - - /* handler: j.CancelCtx type=context.CancelFunc kind=func quoted=false*/ - - { - /* Falling back. type=context.CancelFunc kind=func */ - tbuf, err := fs.CaptureField(tok) - if err != nil { - return fs.WrapErr(err) - } - - err = json.Unmarshal(tbuf, &j.CancelCtx) - if err != nil { - return fs.WrapErr(err) - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_CHFinalize: - - /* handler: j.CHFinalize type=bool kind=bool quoted=false*/ - - { - if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) - } - } - - { - if tok == fflib.FFTok_null { - - } else { - tmpb := fs.Output.Bytes() - - if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { - - j.CHFinalize = true - - } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { - - j.CHFinalize = false - - } else { - err = errors.New("unexpected bytes for true/false value") - return fs.WrapErr(err) - } - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_CHSqlCtx: - - /* handler: j.CHSqlCtx type=sql.Ctx kind=struct quoted=false*/ - - { - /* Falling back. type=sql.Ctx kind=struct */ - tbuf, err := fs.CaptureField(tok) - if err != nil { - return fs.WrapErr(err) - } - - err = json.Unmarshal(tbuf, &j.CHSqlCtx) - if err != nil { - return fs.WrapErr(err) - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_DDBSamplesTable: - - /* handler: j.DDBSamplesTable type=string kind=string quoted=false*/ - - { - - { - if tok != fflib.FFTok_string && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) - } - } - - if tok == fflib.FFTok_null { - - } else { - - outBuf := fs.Output.Bytes() - - j.DDBSamplesTable = string(string(outBuf)) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_DDBTSTable: - - /* handler: j.DDBTSTable type=string kind=string quoted=false*/ - - { - - { - if tok != fflib.FFTok_string && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) - } - } - - if tok == fflib.FFTok_null { - - } else { - - outBuf := fs.Output.Bytes() - - j.DDBTSTable = string(string(outBuf)) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_Step: - - /* handler: j.Step type=time.Duration kind=int64 quoted=false*/ - - { - if tok != fflib.FFTok_integer && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for Duration", tok)) - } - } - - { - - if tok == fflib.FFTok_null { - - } else { - - tval, err := fflib.ParseInt(fs.Output.Bytes(), 10, 64) - - if err != nil { - return fs.WrapErr(err) - } - - j.Step = time.Duration(tval) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_DeleteID: - - /* handler: j.DeleteID type=string kind=string quoted=false*/ - - { - - { - if tok != fflib.FFTok_string && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) - } - } - - if tok == fflib.FFTok_null { - - } else { - - outBuf := fs.Output.Bytes() - - j.DeleteID = string(string(outBuf)) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -wantedvalue: - return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok)) -wrongtokenerror: - return fs.WrapErr(fmt.Errorf("ffjson: wanted token: %v, but got token: %v output=%s", wantedTok, tok, fs.Output.String())) -tokerror: - if fs.BigError != nil { - return fs.WrapErr(fs.BigError) - } - err = fs.Error.ToError() - if err != nil { - return fs.WrapErr(err) - } - panic("ffjson-generated: unreachable, please report bug.") -done: - - return nil -} diff --git a/wasm_parts/traceql/shared/tempo_types.go b/wasm_parts/traceql/shared/tempo_types.go deleted file mode 100644 index bdcb4c1e..00000000 --- a/wasm_parts/traceql/shared/tempo_types.go +++ /dev/null @@ -1,33 +0,0 @@ -package shared - -type TraceInfo struct { - TraceID string `json:"traceID"` - RootServiceName string `json:"rootServiceName"` - RootTraceName string `json:"rootTraceName"` - StartTimeUnixNano string `json:"startTimeUnixNano"` - DurationMs float64 `json:"durationMs"` - SpanSet SpanSet `json:"spanSet"` -} - -type SpanInfo struct { - SpanID string `json:"spanID"` - StartTimeUnixNano string `json:"startTimeUnixNano"` - DurationNanos string `json:"durationNanos"` - Attributes []SpanAttr `json:"attributes"` -} - -type SpanSet struct { - Spans []SpanInfo `json:"spans"` - Matched int `json:"matched"` -} - -type SpanAttr struct { - Key string `json:"key"` - Value struct { - StringValue string `json:"stringValue"` - } `json:"value"` -} - -type TraceRequestProcessor interface { - Process(*PlannerContext) (chan []TraceInfo, error) -} diff --git a/wasm_parts/traceql/shared/types.go b/wasm_parts/traceql/shared/types.go deleted file mode 100644 index cd1c80dd..00000000 --- a/wasm_parts/traceql/shared/types.go +++ /dev/null @@ -1,30 +0,0 @@ -package shared - -import ( - "wasm_parts/sql_select" -) - -type RequestProcessor interface { - IsMatrix() bool - Process(*PlannerContext, chan []LogEntry) (chan []LogEntry, error) -} - -type SQLRequestPlanner interface { - Process(ctx *PlannerContext) (sql.ISelect, error) -} - -type LogEntry struct { - TimestampNS int64 - Fingerprint uint64 - Labels map[string]string - Message string - Value float64 - - Err error -} - -type RequestProcessorChain []RequestProcessor - -type RequestPlanner interface { - Process(cnain RequestProcessorChain) (RequestProcessorChain, error) -} diff --git a/wasm_parts/traceql/transpiler/clickhouse_transpiler/planner.go b/wasm_parts/traceql/transpiler/clickhouse_transpiler/planner.go deleted file mode 100644 index 48cb5639..00000000 --- a/wasm_parts/traceql/transpiler/clickhouse_transpiler/planner.go +++ /dev/null @@ -1,115 +0,0 @@ -package clickhouse_transpiler - -import ( - traceql_parser "wasm_parts/traceql/parser" - "wasm_parts/traceql/shared" -) - -func Plan(script *traceql_parser.TraceQLScript) (shared.SQLRequestPlanner, error) { - return (&planner{script: script}).plan() -} - -type planner struct { - script *traceql_parser.TraceQLScript - - //Analyze results - termIdx []*traceql_parser.AttrSelector - cond *condition - aggFn string - aggAttr string - cmpVal string - - terms map[string]int -} - -func (p *planner) plan() (shared.SQLRequestPlanner, error) { - err := p.check() - if err != nil { - return nil, err - } - - p.analyze() - - var res shared.SQLRequestPlanner = &AttrConditionPlanner{ - Main: &InitIndexPlanner{}, - Terms: p.termIdx, - Conds: p.cond, - AggregatedAttr: p.aggAttr, - } - - res = &IndexGroupByPlanner{res} - - if p.aggFn != "" { - res = &AggregatorPlanner{ - Main: res, - Fn: p.aggFn, - Attr: p.aggAttr, - CompareFn: p.script.Head.Aggregator.Cmp, - CompareVal: p.script.Head.Aggregator.Num + p.script.Head.Aggregator.Measurement, - } - } - - res = &IndexLimitPlanner{res} - - res = &TracesDataPlanner{Main: res} - - res = &IndexLimitPlanner{res} - - return res, nil -} - -func (p *planner) check() error { - if p.script.Tail != nil { - return &shared.NotSupportedError{Msg: "more than one selector not supported"} - } - return nil -} - -func (p *planner) analyze() { - p.terms = make(map[string]int) - p.cond = p.analyzeCond(&p.script.Head.AttrSelector) - p.analyzeAgg() -} - -func (p *planner) analyzeCond(exp *traceql_parser.AttrSelectorExp) *condition { - var res *condition - if exp.ComplexHead != nil { - res = p.analyzeCond(exp.ComplexHead) - } else if exp.Head != nil { - term := exp.Head.String() - if p.terms[term] != 0 { - res = &condition{simpleIdx: p.terms[term] - 1} - } else { - p.termIdx = append(p.termIdx, exp.Head) - p.terms[term] = len(p.termIdx) - res = &condition{simpleIdx: len(p.termIdx) - 1} - } - } - if exp.Tail != nil { - res = &condition{ - simpleIdx: -1, - op: exp.AndOr, - complex: []*condition{res, p.analyzeCond(exp.Tail)}, - } - } - return res -} - -func (p *planner) analyzeAgg() { - if p.script.Head.Aggregator == nil { - return - } - - p.aggFn = p.script.Head.Aggregator.Fn - p.aggAttr = p.script.Head.Aggregator.Attr - - p.cmpVal = p.script.Head.Aggregator.Num + p.script.Head.Aggregator.Measurement - return -} - -type condition struct { - simpleIdx int // index of term; -1 means complex - - op string - complex []*condition -} diff --git a/wasm_parts/traceql/transpiler/clickhouse_transpiler/planner_test.go b/wasm_parts/traceql/transpiler/clickhouse_transpiler/planner_test.go deleted file mode 100644 index b0565251..00000000 --- a/wasm_parts/traceql/transpiler/clickhouse_transpiler/planner_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package clickhouse_transpiler - -import ( - "fmt" - "math/rand" - "testing" - "time" - sql "wasm_parts/sql_select" - traceql_parser "wasm_parts/traceql/parser" - "wasm_parts/traceql/shared" -) - -func TestPlanner(t *testing.T) { - script, err := traceql_parser.Parse(`{.randomContainer=~"admiring" && .randomFloat > 10}`) - if err != nil { - t.Fatal(err) - } - plan, err := Plan(script) - if err != nil { - t.Fatal(err) - } - - req, err := plan.Process(&shared.PlannerContext{ - IsCluster: false, - OrgID: "0", - From: time.Now().Add(time.Hour * -44), - To: time.Now(), - Limit: 3, - TracesAttrsTable: "tempo_traces_attrs_gin", - TracesAttrsDistTable: "tempo_traces_attrs_gin_dist", - TracesTable: "tempo_traces", - TracesDistTable: "tempo_traces_dist", - }) - if err != nil { - t.Fatal(err) - } - res, err := req.String(&sql.Ctx{ - Params: map[string]sql.SQLObject{}, - Result: map[string]sql.SQLObject{}, - }) - if err != nil { - t.Fatal(err) - } - fmt.Println(res) -} - -func TestRandom(t *testing.T) { - fmt.Sprintf("%f", 50+(rand.Float64()*100-50)) -} diff --git a/wasm_parts/traceql/transpiler/clickhouse_transpiler/traces_data.go b/wasm_parts/traceql/transpiler/clickhouse_transpiler/traces_data.go deleted file mode 100644 index 89f70a2d..00000000 --- a/wasm_parts/traceql/transpiler/clickhouse_transpiler/traces_data.go +++ /dev/null @@ -1,50 +0,0 @@ -package clickhouse_transpiler - -import ( - sql "wasm_parts/sql_select" - "wasm_parts/traceql/shared" -) - -type TracesDataPlanner struct { - Main shared.SQLRequestPlanner -} - -func (t *TracesDataPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) { - main, err := t.Main.Process(ctx) - if err != nil { - return nil, err - } - - table := ctx.TracesTable - if ctx.IsCluster { - table = ctx.TracesDistTable - } - - withMain := sql.NewWith(main, "index_grouped") - withTraceIds := sql.NewWith( - sql.NewSelect().Select(sql.NewRawObject("trace_id")).From(sql.NewWithRef(withMain)), - "trace_ids") - return sql.NewSelect(). - With(withMain, withTraceIds). - Select( - sql.NewSimpleCol("lower(hex(traces.trace_id))", "trace_id"), - sql.NewSimpleCol("any(index_grouped.span_id)", "span_id"), - sql.NewSimpleCol("any(index_grouped.duration)", "duration"), - sql.NewSimpleCol("any(index_grouped.timestamp_ns)", "timestamps_ns"), - sql.NewSimpleCol("min(traces.timestamp_ns)", "start_time_unix_nano"), - sql.NewSimpleCol( - "toFloat64(max(traces.timestamp_ns + traces.duration_ns) - min(traces.timestamp_ns)) / 1000000", - "duration_ms"), - sql.NewSimpleCol("argMin(traces.service_name, traces.timestamp_ns)", "root_service_name"), - sql.NewSimpleCol("argMin(traces.name, traces.timestamp_ns)", "root_trace_name"), - ). - From(sql.NewSimpleCol(table, "traces")). - Join(sql.NewJoin("LEFT ANY", - sql.NewWithRef(withMain), - sql.Eq(sql.NewRawObject("traces.trace_id"), sql.NewRawObject("index_grouped.trace_id")))). - AndWhere( - sql.Eq(sql.NewRawObject("oid"), sql.NewStringVal(ctx.OrgID)), - sql.NewIn(sql.NewRawObject("traces.trace_id"), sql.NewWithRef(withTraceIds))). - GroupBy(sql.NewRawObject("traces.trace_id")). - OrderBy(sql.NewOrderBy(sql.NewRawObject("start_time_unix_nano"), sql.ORDER_BY_DIRECTION_DESC)), nil -} diff --git a/wasm_parts/traceql/transpiler/planner.go b/wasm_parts/traceql/transpiler/planner.go deleted file mode 100644 index 1a023a8e..00000000 --- a/wasm_parts/traceql/transpiler/planner.go +++ /dev/null @@ -1,15 +0,0 @@ -package traceql_transpiler - -import ( - traceql_parser "wasm_parts/traceql/parser" - "wasm_parts/traceql/shared" - "wasm_parts/traceql/transpiler/clickhouse_transpiler" -) - -func Plan(script *traceql_parser.TraceQLScript) (shared.SQLRequestPlanner, error) { - sqlPlanner, err := clickhouse_transpiler.Plan(script) - if err != nil { - return nil, err - } - return sqlPlanner, nil -} diff --git a/wasm_parts/types/traceQLRequest.go b/wasm_parts/types/traceQLRequest.go deleted file mode 100644 index 72f94d7d..00000000 --- a/wasm_parts/types/traceQLRequest.go +++ /dev/null @@ -1,10 +0,0 @@ -package types - -import ( - "wasm_parts/traceql/shared" -) - -type TraceQLRequest struct { - Request string - Ctx shared.PlannerContext -} diff --git a/wasm_parts/types/traceQLRequest_ffjson.go b/wasm_parts/types/traceQLRequest_ffjson.go deleted file mode 100644 index 8275d7a5..00000000 --- a/wasm_parts/types/traceQLRequest_ffjson.go +++ /dev/null @@ -1,255 +0,0 @@ -// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT. -// source: traceQLRequest.go - -package types - -import ( - "bytes" - "fmt" - fflib "github.com/pquerna/ffjson/fflib/v1" -) - -// MarshalJSON marshal bytes to json - template -func (j *TraceQLRequest) MarshalJSON() ([]byte, error) { - var buf fflib.Buffer - if j == nil { - buf.WriteString("null") - return buf.Bytes(), nil - } - err := j.MarshalJSONBuf(&buf) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// MarshalJSONBuf marshal buff to json - template -func (j *TraceQLRequest) MarshalJSONBuf(buf fflib.EncodingBuffer) error { - if j == nil { - buf.WriteString("null") - return nil - } - var err error - var obj []byte - _ = obj - _ = err - buf.WriteString(`{"Request":`) - fflib.WriteJsonString(buf, string(j.Request)) - buf.WriteString(`,"Ctx":`) - - { - - err = j.Ctx.MarshalJSONBuf(buf) - if err != nil { - return err - } - - } - buf.WriteByte('}') - return nil -} - -const ( - ffjtTraceQLRequestbase = iota - ffjtTraceQLRequestnosuchkey - - ffjtTraceQLRequestRequest - - ffjtTraceQLRequestCtx -) - -var ffjKeyTraceQLRequestRequest = []byte("Request") - -var ffjKeyTraceQLRequestCtx = []byte("Ctx") - -// UnmarshalJSON umarshall json - template of ffjson -func (j *TraceQLRequest) UnmarshalJSON(input []byte) error { - fs := fflib.NewFFLexer(input) - return j.UnmarshalJSONFFLexer(fs, fflib.FFParse_map_start) -} - -// UnmarshalJSONFFLexer fast json unmarshall - template ffjson -func (j *TraceQLRequest) UnmarshalJSONFFLexer(fs *fflib.FFLexer, state fflib.FFParseState) error { - var err error - currentKey := ffjtTraceQLRequestbase - _ = currentKey - tok := fflib.FFTok_init - wantedTok := fflib.FFTok_init - -mainparse: - for { - tok = fs.Scan() - // println(fmt.Sprintf("debug: tok: %v state: %v", tok, state)) - if tok == fflib.FFTok_error { - goto tokerror - } - - switch state { - - case fflib.FFParse_map_start: - if tok != fflib.FFTok_left_bracket { - wantedTok = fflib.FFTok_left_bracket - goto wrongtokenerror - } - state = fflib.FFParse_want_key - continue - - case fflib.FFParse_after_value: - if tok == fflib.FFTok_comma { - state = fflib.FFParse_want_key - } else if tok == fflib.FFTok_right_bracket { - goto done - } else { - wantedTok = fflib.FFTok_comma - goto wrongtokenerror - } - - case fflib.FFParse_want_key: - // json {} ended. goto exit. woo. - if tok == fflib.FFTok_right_bracket { - goto done - } - if tok != fflib.FFTok_string { - wantedTok = fflib.FFTok_string - goto wrongtokenerror - } - - kn := fs.Output.Bytes() - if len(kn) <= 0 { - // "" case. hrm. - currentKey = ffjtTraceQLRequestnosuchkey - state = fflib.FFParse_want_colon - goto mainparse - } else { - switch kn[0] { - - case 'C': - - if bytes.Equal(ffjKeyTraceQLRequestCtx, kn) { - currentKey = ffjtTraceQLRequestCtx - state = fflib.FFParse_want_colon - goto mainparse - } - - case 'R': - - if bytes.Equal(ffjKeyTraceQLRequestRequest, kn) { - currentKey = ffjtTraceQLRequestRequest - state = fflib.FFParse_want_colon - goto mainparse - } - - } - - if fflib.SimpleLetterEqualFold(ffjKeyTraceQLRequestCtx, kn) { - currentKey = ffjtTraceQLRequestCtx - state = fflib.FFParse_want_colon - goto mainparse - } - - if fflib.EqualFoldRight(ffjKeyTraceQLRequestRequest, kn) { - currentKey = ffjtTraceQLRequestRequest - state = fflib.FFParse_want_colon - goto mainparse - } - - currentKey = ffjtTraceQLRequestnosuchkey - state = fflib.FFParse_want_colon - goto mainparse - } - - case fflib.FFParse_want_colon: - if tok != fflib.FFTok_colon { - wantedTok = fflib.FFTok_colon - goto wrongtokenerror - } - state = fflib.FFParse_want_value - continue - case fflib.FFParse_want_value: - - if tok == fflib.FFTok_left_brace || tok == fflib.FFTok_left_bracket || tok == fflib.FFTok_integer || tok == fflib.FFTok_double || tok == fflib.FFTok_string || tok == fflib.FFTok_bool || tok == fflib.FFTok_null { - switch currentKey { - - case ffjtTraceQLRequestRequest: - goto handle_Request - - case ffjtTraceQLRequestCtx: - goto handle_Ctx - - case ffjtTraceQLRequestnosuchkey: - err = fs.SkipField(tok) - if err != nil { - return fs.WrapErr(err) - } - state = fflib.FFParse_after_value - goto mainparse - } - } else { - goto wantedvalue - } - } - } - -handle_Request: - - /* handler: j.Request type=string kind=string quoted=false*/ - - { - - { - if tok != fflib.FFTok_string && tok != fflib.FFTok_null { - return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) - } - } - - if tok == fflib.FFTok_null { - - } else { - - outBuf := fs.Output.Bytes() - - j.Request = string(string(outBuf)) - - } - } - - state = fflib.FFParse_after_value - goto mainparse - -handle_Ctx: - - /* handler: j.Ctx type=shared.PlannerContext kind=struct quoted=false*/ - - { - if tok == fflib.FFTok_null { - - } else { - - err = j.Ctx.UnmarshalJSONFFLexer(fs, fflib.FFParse_want_key) - if err != nil { - return err - } - } - state = fflib.FFParse_after_value - } - - state = fflib.FFParse_after_value - goto mainparse - -wantedvalue: - return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok)) -wrongtokenerror: - return fs.WrapErr(fmt.Errorf("ffjson: wanted token: %v, but got token: %v output=%s", wantedTok, tok, fs.Output.String())) -tokerror: - if fs.BigError != nil { - return fs.WrapErr(fs.BigError) - } - err = fs.Error.ToError() - if err != nil { - return fs.WrapErr(err) - } - panic("ffjson-generated: unreachable, please report bug.") -done: - - return nil -} diff --git a/wasm_parts/vendor.diff b/wasm_parts/vendor.diff deleted file mode 100644 index de055d90..00000000 --- a/wasm_parts/vendor.diff +++ /dev/null @@ -1,5615 +0,0 @@ -# This is a patch for vendor to update it to _vendor -# -# To apply this patch: -# STEP 1: Chdir to the source directory. -# STEP 2: Run the 'applypatch' program with this patch file as input. -# -# If you do not have 'applypatch', it is part of the 'makepatch' package -# that you can fetch from the Comprehensive Perl Archive Network: -# http://www.perl.com/CPAN/authors/Johan_Vromans/makepatch-x.y.tar.gz -# In the above URL, 'x' should be 2 or higher. -# -# To apply this patch without the use of 'applypatch': -# STEP 1: Chdir to the source directory. -# If you have a decent Bourne-type shell: -# STEP 2: Run the shell with this file as input. -# If you don't have such a shell, you may need to manually create/delete -# the files/directories as shown below. -# STEP 3: Run the 'patch' program with this file as input. -# -# These are the commands needed to create/delete files/directories: -# -mkdir 'github.com/pquerna/ffjson/inception' -chmod 0775 'github.com/pquerna/ffjson/inception' -mkdir 'github.com/pquerna/ffjson/shared' -chmod 0775 'github.com/pquerna/ffjson/shared' -rm -f 'github.com/prometheus/prometheus/util/teststorage/storage.go' -rm -f 'github.com/prometheus/prometheus/tsdb/wal/watcher.go' -rm -f 'github.com/prometheus/prometheus/tsdb/wal/wal.go' -rm -f 'github.com/prometheus/prometheus/tsdb/wal/reader.go' -rm -f 'github.com/prometheus/prometheus/tsdb/wal/live_reader.go' -rm -f 'github.com/prometheus/prometheus/tsdb/wal/checkpoint.go' -rm -f 'github.com/prometheus/prometheus/tsdb/wal.go' -rm -f 'github.com/prometheus/prometheus/tsdb/tsdbutil/dir_locker_testutil.go' -rm -f 'github.com/prometheus/prometheus/tsdb/tsdbutil/dir_locker.go' -rm -f 'github.com/prometheus/prometheus/tsdb/tsdbutil/chunks.go' -rm -f 'github.com/prometheus/prometheus/tsdb/tsdbutil/buffer.go' -rm -f 'github.com/prometheus/prometheus/tsdb/tsdbblockutil.go' -rm -f 'github.com/prometheus/prometheus/tsdb/tombstones/tombstones.go' -rm -f 'github.com/prometheus/prometheus/tsdb/repair.go' -rm -f 'github.com/prometheus/prometheus/tsdb/record/record.go' -rm -f 'github.com/prometheus/prometheus/tsdb/querier.go' -rm -f 'github.com/prometheus/prometheus/tsdb/isolation.go' -rm -f 'github.com/prometheus/prometheus/tsdb/index/postingsstats.go' -rm -f 'github.com/prometheus/prometheus/tsdb/index/postings.go' -rm -f 'github.com/prometheus/prometheus/tsdb/index/index.go' -rm -f 'github.com/prometheus/prometheus/tsdb/head_wal.go' -rm -f 'github.com/prometheus/prometheus/tsdb/head_read.go' -rm -f 'github.com/prometheus/prometheus/tsdb/head_append.go' -rm -f 'github.com/prometheus/prometheus/tsdb/head.go' -rm -f 'github.com/prometheus/prometheus/tsdb/goversion/init.go' -rm -f 'github.com/prometheus/prometheus/tsdb/goversion/goversion.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/sync_linux.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/sync_darwin.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/sync.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/preallocate_other.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/preallocate_linux.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/preallocate_darwin.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/preallocate.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/mmap_windows.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/mmap_unix.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/mmap_js.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/mmap_arm64.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/mmap_amd64.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/mmap_386.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/mmap.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/flock_windows.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/flock_unix.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/flock_solaris.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/flock_plan9.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/flock_js.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/flock.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/fileutil.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/dir_windows.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/dir_unix.go' -rm -f 'github.com/prometheus/prometheus/tsdb/fileutil/dir.go' -rm -f 'github.com/prometheus/prometheus/tsdb/exemplar.go' -rm -f 'github.com/prometheus/prometheus/tsdb/errors/errors.go' -rm -f 'github.com/prometheus/prometheus/tsdb/encoding/encoding.go' -rm -f 'github.com/prometheus/prometheus/tsdb/db.go' -rm -f 'github.com/prometheus/prometheus/tsdb/compact.go' -rm -f 'github.com/prometheus/prometheus/tsdb/chunks/queue.go' -rm -f 'github.com/prometheus/prometheus/tsdb/chunks/head_chunks_windows.go' -rm -f 'github.com/prometheus/prometheus/tsdb/chunks/head_chunks_other.go' -rm -f 'github.com/prometheus/prometheus/tsdb/chunks/head_chunks.go' -rm -f 'github.com/prometheus/prometheus/tsdb/chunks/chunk_write_queue.go' -rm -f 'github.com/prometheus/prometheus/tsdb/blockwriter.go' -rm -f 'github.com/prometheus/prometheus/tsdb/block.go' -rm -f 'github.com/prometheus/prometheus/tsdb/README.md' -rm -f 'github.com/prometheus/prometheus/tsdb/CHANGELOG.md' -rm -f 'github.com/prometheus/prometheus/tsdb/.gitignore' -rm -f 'github.com/prometheus/prometheus/storage/series.go' -rm -f 'github.com/prometheus/prometheus/storage/secondary.go' -rm -f 'github.com/prometheus/prometheus/storage/merge.go' -rm -f 'github.com/prometheus/prometheus/storage/lazy.go' -rm -f 'github.com/prometheus/prometheus/storage/fanout.go' -rm -f 'github.com/prometheus/prometheus/promql/test.go' -rm -f 'github.com/prometheus/prometheus/promql/query_logger.go' -touch 'github.com/pquerna/ffjson/inception/decoder.go' -chmod 0664 'github.com/pquerna/ffjson/inception/decoder.go' -touch 'github.com/pquerna/ffjson/inception/decoder_tpl.go' -chmod 0664 'github.com/pquerna/ffjson/inception/decoder_tpl.go' -touch 'github.com/pquerna/ffjson/inception/encoder.go' -chmod 0664 'github.com/pquerna/ffjson/inception/encoder.go' -touch 'github.com/pquerna/ffjson/inception/encoder_tpl.go' -chmod 0664 'github.com/pquerna/ffjson/inception/encoder_tpl.go' -touch 'github.com/pquerna/ffjson/inception/inception.go' -chmod 0664 'github.com/pquerna/ffjson/inception/inception.go' -touch 'github.com/pquerna/ffjson/inception/reflect.go' -chmod 0664 'github.com/pquerna/ffjson/inception/reflect.go' -touch 'github.com/pquerna/ffjson/inception/tags.go' -chmod 0664 'github.com/pquerna/ffjson/inception/tags.go' -touch 'github.com/pquerna/ffjson/inception/template.go' -chmod 0664 'github.com/pquerna/ffjson/inception/template.go' -touch 'github.com/pquerna/ffjson/inception/writerstack.go' -chmod 0664 'github.com/pquerna/ffjson/inception/writerstack.go' -touch 'github.com/pquerna/ffjson/shared/options.go' -chmod 0664 'github.com/pquerna/ffjson/shared/options.go' -rmdir 'github.com/prometheus/prometheus/util/teststorage' -rmdir 'github.com/prometheus/prometheus/tsdb/wal' -rmdir 'github.com/prometheus/prometheus/tsdb/tsdbutil' -rmdir 'github.com/prometheus/prometheus/tsdb/tombstones' -rmdir 'github.com/prometheus/prometheus/tsdb/record' -rmdir 'github.com/prometheus/prometheus/tsdb/index' -rmdir 'github.com/prometheus/prometheus/tsdb/goversion' -rmdir 'github.com/prometheus/prometheus/tsdb/fileutil' -rmdir 'github.com/prometheus/prometheus/tsdb/errors' -rmdir 'github.com/prometheus/prometheus/tsdb/encoding' -# -# This command terminates the shell and need not be executed manually. -exit -# -#### End of Preamble #### - -#### Patch data follows #### -diff -c 'vendor/github.com/alecthomas/participle/v2/validate.go' '_vendor/github.com/alecthomas/participle/v2/validate.go' -Index: ./github.com/alecthomas/participle/v2/validate.go -*** ./github.com/alecthomas/participle/v2/validate.go Mon Mar 11 19:34:50 2024 ---- ./github.com/alecthomas/participle/v2/validate.go Thu Oct 26 15:52:53 2023 -*************** -*** 42,48 **** - - case *sequence: - if !n.head { -! panic("done") - } - } - if seen[n] { ---- 42,48 ---- - - case *sequence: - if !n.head { -! return nil - } - } - if seen[n] { -diff -c 'vendor/github.com/aws/aws-sdk-go/aws/defaults/defaults.go' '_vendor/github.com/aws/aws-sdk-go/aws/defaults/defaults.go' -Index: ./github.com/aws/aws-sdk-go/aws/defaults/defaults.go -*** ./github.com/aws/aws-sdk-go/aws/defaults/defaults.go Mon Mar 11 19:34:50 2024 ---- ./github.com/aws/aws-sdk-go/aws/defaults/defaults.go Thu Oct 26 15:21:07 2023 -*************** -*** 132,138 **** - return ec2RoleProvider(cfg, handlers) - } - -! var lookupHostFn = net.LookupHost - - func isLoopbackHost(host string) (bool, error) { - ip := net.ParseIP(host) ---- 132,140 ---- - return ec2RoleProvider(cfg, handlers) - } - -! var lookupHostFn = func (string) ([]string, error) { -! return nil, nil -! } - - func isLoopbackHost(host string) (bool, error) { - ip := net.ParseIP(host) -diff -c 'vendor/github.com/aws/aws-sdk-go/aws/request/retryer.go' '_vendor/github.com/aws/aws-sdk-go/aws/request/retryer.go' -Index: ./github.com/aws/aws-sdk-go/aws/request/retryer.go -*** ./github.com/aws/aws-sdk-go/aws/request/retryer.go Mon Mar 11 19:34:50 2024 ---- ./github.com/aws/aws-sdk-go/aws/request/retryer.go Thu Oct 26 15:21:07 2023 -*************** -*** 1,7 **** - package request - - import ( -- "net" - "net/url" - "strings" - "time" ---- 1,6 ---- -*************** -*** 200,208 **** - return shouldRetryError(err.Err) - - case temporary: -- if netErr, ok := err.(*net.OpError); ok && netErr.Op == "dial" { -- return true -- } - // If the error is temporary, we want to allow continuation of the - // retry process - return err.Temporary() || isErrConnectionReset(origErr) ---- 199,204 ---- -diff -c 'vendor/github.com/davecgh/go-spew/spew/bypass.go' '_vendor/github.com/davecgh/go-spew/spew/bypass.go' -Index: ./github.com/davecgh/go-spew/spew/bypass.go -*** ./github.com/davecgh/go-spew/spew/bypass.go Mon Mar 11 19:34:50 2024 ---- ./github.com/davecgh/go-spew/spew/bypass.go Thu Oct 26 15:21:07 2023 -*************** -*** 68,80 **** - addr: 1 << 8, - }} - -! var flagValOffset = func() uintptr { -! field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") -! if !ok { -! panic("reflect.Value has no flag field") -! } -! return field.Offset -! }() - - // flagField returns a pointer to the flag field of a reflect.Value. - func flagField(v *reflect.Value) *flag { ---- 68,74 ---- - addr: 1 << 8, - }} - -! var flagValOffset = (uintptr)(0) - - // flagField returns a pointer to the flag field of a reflect.Value. - func flagField(v *reflect.Value) *flag { -*************** -*** 102,145 **** - - // Sanity checks against future reflect package changes - // to the type or semantics of the Value.flag field. -- func init() { -- field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") -- if !ok { -- panic("reflect.Value has no flag field") -- } -- if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { -- panic("reflect.Value flag field has changed kind") -- } -- type t0 int -- var t struct { -- A t0 -- // t0 will have flagEmbedRO set. -- t0 -- // a will have flagStickyRO set -- a t0 -- } -- vA := reflect.ValueOf(t).FieldByName("A") -- va := reflect.ValueOf(t).FieldByName("a") -- vt0 := reflect.ValueOf(t).FieldByName("t0") -- -- // Infer flagRO from the difference between the flags -- // for the (otherwise identical) fields in t. -- flagPublic := *flagField(&vA) -- flagWithRO := *flagField(&va) | *flagField(&vt0) -- flagRO = flagPublic ^ flagWithRO -- -- // Infer flagAddr from the difference between a value -- // taken from a pointer and not. -- vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") -- flagNoPtr := *flagField(&vA) -- flagPtr := *flagField(&vPtrA) -- flagAddr = flagNoPtr ^ flagPtr -- -- // Check that the inferred flags tally with one of the known versions. -- for _, f := range okFlags { -- if flagRO == f.ro && flagAddr == f.addr { -- return -- } -- } -- panic("reflect.Value read-only flag has changed semantics") -- } ---- 96,98 ---- -diff -c 'vendor/github.com/edsrzf/mmap-go/mmap.go' '_vendor/github.com/edsrzf/mmap-go/mmap.go' -Index: ./github.com/edsrzf/mmap-go/mmap.go -*** ./github.com/edsrzf/mmap-go/mmap.go Mon Mar 11 19:34:50 2024 ---- ./github.com/edsrzf/mmap-go/mmap.go Thu Oct 26 15:21:07 2023 -*************** -*** 15,21 **** - package mmap - - import ( -- "errors" - "os" - "reflect" - "unsafe" ---- 15,20 ---- -*************** -*** 54,80 **** - // If length < 0, the entire file will be mapped. - // If ANON is set in flags, f is ignored. - func MapRegion(f *os.File, length int, prot, flags int, offset int64) (MMap, error) { -! if offset%int64(os.Getpagesize()) != 0 { -! return nil, errors.New("offset parameter must be a multiple of the system's page size") -! } -! -! var fd uintptr -! if flags&ANON == 0 { -! fd = uintptr(f.Fd()) -! if length < 0 { -! fi, err := f.Stat() -! if err != nil { -! return nil, err -! } -! length = int(fi.Size()) -! } -! } else { -! if length <= 0 { -! return nil, errors.New("anonymous mapping requires non-zero length") -! } -! fd = ^uintptr(0) -! } -! return mmap(length, uintptr(prot), uintptr(flags), fd, offset) - } - - func (m *MMap) header() *reflect.SliceHeader { ---- 53,59 ---- - // If length < 0, the entire file will be mapped. - // If ANON is set in flags, f is ignored. - func MapRegion(f *os.File, length int, prot, flags int, offset int64) (MMap, error) { -! return nil, nil - } - - func (m *MMap) header() *reflect.SliceHeader { -*************** -*** 89,107 **** - // Lock keeps the mapped region in physical memory, ensuring that it will not be - // swapped out. - func (m MMap) Lock() error { -! return m.lock() - } - - // Unlock reverses the effect of Lock, allowing the mapped region to potentially - // be swapped out. - // If m is already unlocked, aan error will result. - func (m MMap) Unlock() error { -! return m.unlock() - } - - // Flush synchronizes the mapping's contents to the file's contents on disk. - func (m MMap) Flush() error { -! return m.flush() - } - - // Unmap deletes the memory mapped region, flushes any remaining changes, and sets ---- 68,86 ---- - // Lock keeps the mapped region in physical memory, ensuring that it will not be - // swapped out. - func (m MMap) Lock() error { -! return nil - } - - // Unlock reverses the effect of Lock, allowing the mapped region to potentially - // be swapped out. - // If m is already unlocked, aan error will result. - func (m MMap) Unlock() error { -! return nil - } - - // Flush synchronizes the mapping's contents to the file's contents on disk. - func (m MMap) Flush() error { -! return nil - } - - // Unmap deletes the memory mapped region, flushes any remaining changes, and sets -*************** -*** 111,117 **** - // Unmap should only be called on the slice value that was originally returned from - // a call to Map. Calling Unmap on a derived slice may cause errors. - func (m *MMap) Unmap() error { -! err := m.unmap() -! *m = nil -! return err - } ---- 90,94 ---- - // Unmap should only be called on the slice value that was originally returned from - // a call to Map. Calling Unmap on a derived slice may cause errors. - func (m *MMap) Unmap() error { -! return nil - } -diff -c 'vendor/github.com/grafana/regexp/backtrack.go' '_vendor/github.com/grafana/regexp/backtrack.go' -Index: ./github.com/grafana/regexp/backtrack.go -*** ./github.com/grafana/regexp/backtrack.go Mon Mar 11 19:34:50 2024 ---- ./github.com/grafana/regexp/backtrack.go Mon Mar 11 11:17:18 2024 -*************** -*** 16,22 **** - - import ( - "regexp/syntax" -- "sync" - ) - - // A job is an entry on the backtracker's job stack. It holds ---- 16,21 ---- -*************** -*** 44,50 **** - inputs inputs - } - -! var bitStatePool sync.Pool - - func newBitState() *bitState { - b, ok := bitStatePool.Get().(*bitState) ---- 43,53 ---- - inputs inputs - } - -! type fakePool[T any] struct {} -! func (f fakePool[T]) Get() interface{} { return new(T) } -! func (f fakePool[T]) Put(x any) { } -! -! var bitStatePool fakePool[bitState] - - func newBitState() *bitState { - b, ok := bitStatePool.Get().(*bitState) -diff -c 'vendor/github.com/grafana/regexp/exec.go' '_vendor/github.com/grafana/regexp/exec.go' -Index: ./github.com/grafana/regexp/exec.go -*** ./github.com/grafana/regexp/exec.go Mon Mar 11 19:34:50 2024 ---- ./github.com/grafana/regexp/exec.go Mon Mar 11 11:17:07 2024 -*************** -*** 7,13 **** - import ( - "io" - "regexp/syntax" -- "sync" - ) - - // A queue is a 'sparse array' holding pending threads of execution. ---- 7,12 ---- -*************** -*** 377,384 **** - inputs inputs - matchcap []int - } -! -! var onePassPool sync.Pool - - func newOnePassMachine() *onePassMachine { - m, ok := onePassPool.Get().(*onePassMachine) ---- 376,382 ---- - inputs inputs - matchcap []int - } -! var onePassPool fakePool[onePassMachine] - - func newOnePassMachine() *onePassMachine { - m, ok := onePassPool.Get().(*onePassMachine) -diff -c 'vendor/github.com/grafana/regexp/regexp.go' '_vendor/github.com/grafana/regexp/regexp.go' -Index: ./github.com/grafana/regexp/regexp.go -*** ./github.com/grafana/regexp/regexp.go Mon Mar 11 19:34:50 2024 ---- ./github.com/grafana/regexp/regexp.go Mon Mar 11 18:14:29 2024 -*************** -*** 71,77 **** - "regexp/syntax" - "strconv" - "strings" -- "sync" - "unicode" - "unicode/utf8" - ) ---- 71,76 ---- -*************** -*** 225,231 **** - // The final matchPool is a catch-all for very large queues. - var ( - matchSize = [...]int{128, 512, 2048, 16384, 0} -! matchPool [len(matchSize)]sync.Pool - ) - - // get returns a machine to use for matching re. ---- 224,230 ---- - // The final matchPool is a catch-all for very large queues. - var ( - matchSize = [...]int{128, 512, 2048, 16384, 0} -! matchPool [len(matchSize)]fakePool[machine] - ) - - // get returns a machine to use for matching re. -diff -c 'vendor/github.com/mwitkow/go-conntrack/dialer_reporter.go' '_vendor/github.com/mwitkow/go-conntrack/dialer_reporter.go' -Index: ./github.com/mwitkow/go-conntrack/dialer_reporter.go -*** ./github.com/mwitkow/go-conntrack/dialer_reporter.go Mon Mar 11 19:34:50 2024 ---- ./github.com/mwitkow/go-conntrack/dialer_reporter.go Thu Oct 26 15:21:07 2023 -*************** -*** 87,95 **** - func reportDialerConnFailed(dialerName string, err error) { - if netErr, ok := err.(*net.OpError); ok { - switch nestErr := netErr.Err.(type) { -- case *net.DNSError: -- dialerConnFailedTotal.WithLabelValues(dialerName, string(failedResolution)).Inc() -- return - case *os.SyscallError: - if nestErr.Err == syscall.ECONNREFUSED { - dialerConnFailedTotal.WithLabelValues(dialerName, string(failedConnRefused)).Inc() ---- 87,92 ---- -*************** -*** 97,105 **** - dialerConnFailedTotal.WithLabelValues(dialerName, string(failedUnknown)).Inc() - return - } -- if netErr.Timeout() { -- dialerConnFailedTotal.WithLabelValues(dialerName, string(failedTimeout)).Inc() -- } - } else if err == context.Canceled || err == context.DeadlineExceeded { - dialerConnFailedTotal.WithLabelValues(dialerName, string(failedTimeout)).Inc() - return ---- 94,99 ---- -diff -c 'vendor/github.com/mwitkow/go-conntrack/listener_wrapper.go' '_vendor/github.com/mwitkow/go-conntrack/listener_wrapper.go' -Index: ./github.com/mwitkow/go-conntrack/listener_wrapper.go -*** ./github.com/mwitkow/go-conntrack/listener_wrapper.go Mon Mar 11 19:34:50 2024 ---- ./github.com/mwitkow/go-conntrack/listener_wrapper.go Thu Oct 26 15:21:07 2023 -*************** -*** 109,118 **** - if err != nil { - return nil, err - } -- if tcpConn, ok := conn.(*net.TCPConn); ok && ct.opts.tcpKeepAlive > 0 { -- tcpConn.SetKeepAlive(true) -- tcpConn.SetKeepAlivePeriod(ct.opts.tcpKeepAlive) -- } - return newServerConnTracker(conn, ct.opts), nil - } - ---- 109,114 ---- -diff -c 'vendor/github.com/pquerna/ffjson/fflib/v1/buffer_pool.go' '_vendor/github.com/pquerna/ffjson/fflib/v1/buffer_pool.go' -Index: ./github.com/pquerna/ffjson/fflib/v1/buffer_pool.go -*** ./github.com/pquerna/ffjson/fflib/v1/buffer_pool.go Mon Mar 11 19:34:50 2024 ---- ./github.com/pquerna/ffjson/fflib/v1/buffer_pool.go Mon Mar 11 18:36:36 2024 -*************** -*** 8,17 **** - - // Allocation pools for Buffers. - -! import "sync" - -! var pools [14]sync.Pool -! var pool64 *sync.Pool - - func init() { - var i uint ---- 8,22 ---- - - // Allocation pools for Buffers. - -! type fakePool struct { -! New func() any -! } - -! func (f *fakePool) Get() any { return f.New() } -! func (f *fakePool) Put(x any) {} -! -! var pools [14]fakePool -! var pool64 *fakePool - - func init() { - var i uint -diff -c /dev/null '_vendor/github.com/pquerna/ffjson/inception/decoder.go' -Index: ./github.com/pquerna/ffjson/inception/decoder.go -*** ./github.com/pquerna/ffjson/inception/decoder.go Thu Jan 1 03:00:00 1970 ---- ./github.com/pquerna/ffjson/inception/decoder.go Thu Oct 26 14:17:41 2023 -*************** -*** 0 **** ---- 1,323 ---- -+ /** -+ * Copyright 2014 Paul Querna -+ * -+ * Licensed under the Apache License, Version 2.0 (the "License"); -+ * you may not use this file except in compliance with the License. -+ * You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, software -+ * distributed under the License is distributed on an "AS IS" BASIS, -+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ * See the License for the specific language governing permissions and -+ * limitations under the License. -+ * -+ */ -+ -+ package ffjsoninception -+ -+ import ( -+ "fmt" -+ "reflect" -+ "strings" -+ -+ "github.com/pquerna/ffjson/shared" -+ ) -+ -+ var validValues []string = []string{ -+ "FFTok_left_brace", -+ "FFTok_left_bracket", -+ "FFTok_integer", -+ "FFTok_double", -+ "FFTok_string", -+ "FFTok_bool", -+ "FFTok_null", -+ } -+ -+ func CreateUnmarshalJSON(ic *Inception, si *StructInfo) error { -+ out := "" -+ ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true -+ if len(si.Fields) > 0 { -+ ic.OutputImports[`"bytes"`] = true -+ } -+ ic.OutputImports[`"fmt"`] = true -+ -+ out += tplStr(decodeTpl["header"], header{ -+ IC: ic, -+ SI: si, -+ }) -+ -+ out += tplStr(decodeTpl["ujFunc"], ujFunc{ -+ SI: si, -+ IC: ic, -+ ValidValues: validValues, -+ ResetFields: ic.ResetFields, -+ }) -+ -+ ic.OutputFuncs = append(ic.OutputFuncs, out) -+ -+ return nil -+ } -+ -+ func handleField(ic *Inception, name string, typ reflect.Type, ptr bool, quoted bool) string { -+ return handleFieldAddr(ic, name, false, typ, ptr, quoted) -+ } -+ -+ func handleFieldAddr(ic *Inception, name string, takeAddr bool, typ reflect.Type, ptr bool, quoted bool) string { -+ out := fmt.Sprintf("/* handler: %s type=%v kind=%v quoted=%t*/\n", name, typ, typ.Kind(), quoted) -+ -+ umlx := typ.Implements(unmarshalFasterType) || typeInInception(ic, typ, shared.MustDecoder) -+ umlx = umlx || reflect.PtrTo(typ).Implements(unmarshalFasterType) -+ -+ umlstd := typ.Implements(unmarshalerType) || reflect.PtrTo(typ).Implements(unmarshalerType) -+ -+ out += tplStr(decodeTpl["handleUnmarshaler"], handleUnmarshaler{ -+ IC: ic, -+ Name: name, -+ Typ: typ, -+ Ptr: reflect.Ptr, -+ TakeAddr: takeAddr || ptr, -+ UnmarshalJSONFFLexer: umlx, -+ Unmarshaler: umlstd, -+ }) -+ -+ if umlx || umlstd { -+ return out -+ } -+ -+ // TODO(pquerna): generic handling of token type mismatching struct type -+ switch typ.Kind() { -+ case reflect.Int, -+ reflect.Int8, -+ reflect.Int16, -+ reflect.Int32, -+ reflect.Int64: -+ -+ allowed := buildTokens(quoted, "FFTok_string", "FFTok_integer", "FFTok_null") -+ out += getAllowTokens(typ.Name(), allowed...) -+ -+ out += getNumberHandler(ic, name, takeAddr || ptr, typ, "ParseInt") -+ -+ case reflect.Uint, -+ reflect.Uint8, -+ reflect.Uint16, -+ reflect.Uint32, -+ reflect.Uint64: -+ -+ allowed := buildTokens(quoted, "FFTok_string", "FFTok_integer", "FFTok_null") -+ out += getAllowTokens(typ.Name(), allowed...) -+ -+ out += getNumberHandler(ic, name, takeAddr || ptr, typ, "ParseUint") -+ -+ case reflect.Float32, -+ reflect.Float64: -+ -+ allowed := buildTokens(quoted, "FFTok_string", "FFTok_double", "FFTok_integer", "FFTok_null") -+ out += getAllowTokens(typ.Name(), allowed...) -+ -+ out += getNumberHandler(ic, name, takeAddr || ptr, typ, "ParseFloat") -+ -+ case reflect.Bool: -+ ic.OutputImports[`"bytes"`] = true -+ ic.OutputImports[`"errors"`] = true -+ -+ allowed := buildTokens(quoted, "FFTok_string", "FFTok_bool", "FFTok_null") -+ out += getAllowTokens(typ.Name(), allowed...) -+ -+ out += tplStr(decodeTpl["handleBool"], handleBool{ -+ Name: name, -+ Typ: typ, -+ TakeAddr: takeAddr || ptr, -+ }) -+ -+ case reflect.Ptr: -+ out += tplStr(decodeTpl["handlePtr"], handlePtr{ -+ IC: ic, -+ Name: name, -+ Typ: typ, -+ Quoted: quoted, -+ }) -+ -+ case reflect.Array, -+ reflect.Slice: -+ out += getArrayHandler(ic, name, typ, ptr) -+ -+ case reflect.String: -+ // Is it a json.Number? -+ if typ.PkgPath() == "encoding/json" && typ.Name() == "Number" { -+ // Fall back to json package to rely on the valid number check. -+ // See: https://github.com/golang/go/blob/f05c3aa24d815cd3869153750c9875e35fc48a6e/src/encoding/json/decode.go#L897 -+ ic.OutputImports[`"encoding/json"`] = true -+ out += tplStr(decodeTpl["handleFallback"], handleFallback{ -+ Name: name, -+ Typ: typ, -+ Kind: typ.Kind(), -+ }) -+ } else { -+ out += tplStr(decodeTpl["handleString"], handleString{ -+ IC: ic, -+ Name: name, -+ Typ: typ, -+ TakeAddr: takeAddr || ptr, -+ Quoted: quoted, -+ }) -+ } -+ case reflect.Interface: -+ ic.OutputImports[`"encoding/json"`] = true -+ out += tplStr(decodeTpl["handleFallback"], handleFallback{ -+ Name: name, -+ Typ: typ, -+ Kind: typ.Kind(), -+ }) -+ case reflect.Map: -+ out += tplStr(decodeTpl["handleObject"], handleObject{ -+ IC: ic, -+ Name: name, -+ Typ: typ, -+ Ptr: reflect.Ptr, -+ TakeAddr: takeAddr || ptr, -+ }) -+ default: -+ ic.OutputImports[`"encoding/json"`] = true -+ out += tplStr(decodeTpl["handleFallback"], handleFallback{ -+ Name: name, -+ Typ: typ, -+ Kind: typ.Kind(), -+ }) -+ } -+ -+ return out -+ } -+ -+ func getArrayHandler(ic *Inception, name string, typ reflect.Type, ptr bool) string { -+ if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 { -+ ic.OutputImports[`"encoding/base64"`] = true -+ useReflectToSet := false -+ if typ.Elem().Name() != "byte" { -+ ic.OutputImports[`"reflect"`] = true -+ useReflectToSet = true -+ } -+ -+ return tplStr(decodeTpl["handleByteSlice"], handleArray{ -+ IC: ic, -+ Name: name, -+ Typ: typ, -+ Ptr: reflect.Ptr, -+ UseReflectToSet: useReflectToSet, -+ }) -+ } -+ -+ if typ.Elem().Kind() == reflect.Struct && typ.Elem().Name() != "" { -+ goto sliceOrArray -+ } -+ -+ if (typ.Elem().Kind() == reflect.Struct || typ.Elem().Kind() == reflect.Map) || -+ typ.Elem().Kind() == reflect.Array || typ.Elem().Kind() == reflect.Slice && -+ typ.Elem().Name() == "" { -+ ic.OutputImports[`"encoding/json"`] = true -+ -+ return tplStr(decodeTpl["handleFallback"], handleFallback{ -+ Name: name, -+ Typ: typ, -+ Kind: typ.Kind(), -+ }) -+ } -+ -+ sliceOrArray: -+ -+ if typ.Kind() == reflect.Array { -+ return tplStr(decodeTpl["handleArray"], handleArray{ -+ IC: ic, -+ Name: name, -+ Typ: typ, -+ IsPtr: ptr, -+ Ptr: reflect.Ptr, -+ }) -+ } -+ -+ return tplStr(decodeTpl["handleSlice"], handleArray{ -+ IC: ic, -+ Name: name, -+ Typ: typ, -+ IsPtr: ptr, -+ Ptr: reflect.Ptr, -+ }) -+ } -+ -+ func getAllowTokens(name string, tokens ...string) string { -+ return tplStr(decodeTpl["allowTokens"], allowTokens{ -+ Name: name, -+ Tokens: tokens, -+ }) -+ } -+ -+ func getNumberHandler(ic *Inception, name string, takeAddr bool, typ reflect.Type, parsefunc string) string { -+ return tplStr(decodeTpl["handlerNumeric"], handlerNumeric{ -+ IC: ic, -+ Name: name, -+ ParseFunc: parsefunc, -+ TakeAddr: takeAddr, -+ Typ: typ, -+ }) -+ } -+ -+ func getNumberSize(typ reflect.Type) string { -+ return fmt.Sprintf("%d", typ.Bits()) -+ } -+ -+ func getType(ic *Inception, name string, typ reflect.Type) string { -+ s := typ.Name() -+ -+ if typ.PkgPath() != "" && typ.PkgPath() != ic.PackagePath { -+ path := removeVendor(typ.PkgPath()) -+ ic.OutputImports[`"`+path+`"`] = true -+ s = typ.String() -+ } -+ -+ if s == "" { -+ return typ.String() -+ } -+ -+ return s -+ } -+ -+ // removeVendor removes everything before and including a '/vendor/' -+ // substring in the package path. -+ // This is needed becuase that full path can't be used in the -+ // import statement. -+ func removeVendor(path string) string { -+ i := strings.Index(path, "/vendor/") -+ if i == -1 { -+ return path -+ } -+ return path[i+8:] -+ } -+ -+ func buildTokens(containsOptional bool, optional string, required ...string) []string { -+ if containsOptional { -+ return append(required, optional) -+ } -+ -+ return required -+ } -+ -+ func unquoteField(quoted bool) string { -+ // The outer quote of a string is already stripped out by -+ // the lexer. We need to check if the inner string is also -+ // quoted. If so, we will decode it as json string. If decoding -+ // fails, we will use the original string -+ if quoted { -+ return ` -+ unquoted, ok := fflib.UnquoteBytes(outBuf) -+ if ok { -+ outBuf = unquoted -+ } -+ ` -+ } -+ return "" -+ } -+ -+ func getTmpVarFor(name string) string { -+ return "tmp" + strings.Replace(strings.Title(name), ".", "", -1) -+ } -diff -c /dev/null '_vendor/github.com/pquerna/ffjson/inception/decoder_tpl.go' -Index: ./github.com/pquerna/ffjson/inception/decoder_tpl.go -*** ./github.com/pquerna/ffjson/inception/decoder_tpl.go Thu Jan 1 03:00:00 1970 ---- ./github.com/pquerna/ffjson/inception/decoder_tpl.go Thu Oct 26 14:17:41 2023 -*************** -*** 0 **** ---- 1,773 ---- -+ /** -+ * Copyright 2014 Paul Querna -+ * -+ * Licensed under the Apache License, Version 2.0 (the "License"); -+ * you may not use this file except in compliance with the License. -+ * You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, software -+ * distributed under the License is distributed on an "AS IS" BASIS, -+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ * See the License for the specific language governing permissions and -+ * limitations under the License. -+ * -+ */ -+ -+ package ffjsoninception -+ -+ import ( -+ "reflect" -+ "strconv" -+ "text/template" -+ ) -+ -+ var decodeTpl map[string]*template.Template -+ -+ func init() { -+ decodeTpl = make(map[string]*template.Template) -+ -+ funcs := map[string]string{ -+ "handlerNumeric": handlerNumericTxt, -+ "allowTokens": allowTokensTxt, -+ "handleFallback": handleFallbackTxt, -+ "handleString": handleStringTxt, -+ "handleObject": handleObjectTxt, -+ "handleArray": handleArrayTxt, -+ "handleSlice": handleSliceTxt, -+ "handleByteSlice": handleByteSliceTxt, -+ "handleBool": handleBoolTxt, -+ "handlePtr": handlePtrTxt, -+ "header": headerTxt, -+ "ujFunc": ujFuncTxt, -+ "handleUnmarshaler": handleUnmarshalerTxt, -+ } -+ -+ tplFuncs := template.FuncMap{ -+ "getAllowTokens": getAllowTokens, -+ "getNumberSize": getNumberSize, -+ "getType": getType, -+ "handleField": handleField, -+ "handleFieldAddr": handleFieldAddr, -+ "unquoteField": unquoteField, -+ "getTmpVarFor": getTmpVarFor, -+ } -+ -+ for k, v := range funcs { -+ decodeTpl[k] = template.Must(template.New(k).Funcs(tplFuncs).Parse(v)) -+ } -+ } -+ -+ type handlerNumeric struct { -+ IC *Inception -+ Name string -+ ParseFunc string -+ Typ reflect.Type -+ TakeAddr bool -+ } -+ -+ var handlerNumericTxt = ` -+ { -+ {{$ic := .IC}} -+ -+ if tok == fflib.FFTok_null { -+ {{if eq .TakeAddr true}} -+ {{.Name}} = nil -+ {{end}} -+ } else { -+ {{if eq .ParseFunc "ParseFloat" }} -+ tval, err := fflib.{{ .ParseFunc}}(fs.Output.Bytes(), {{getNumberSize .Typ}}) -+ {{else}} -+ tval, err := fflib.{{ .ParseFunc}}(fs.Output.Bytes(), 10, {{getNumberSize .Typ}}) -+ {{end}} -+ -+ if err != nil { -+ return fs.WrapErr(err) -+ } -+ {{if eq .TakeAddr true}} -+ ttypval := {{getType $ic .Name .Typ}}(tval) -+ {{.Name}} = &ttypval -+ {{else}} -+ {{.Name}} = {{getType $ic .Name .Typ}}(tval) -+ {{end}} -+ } -+ } -+ ` -+ -+ type allowTokens struct { -+ Name string -+ Tokens []string -+ } -+ -+ var allowTokensTxt = ` -+ { -+ if {{range $index, $element := .Tokens}}{{if ne $index 0 }}&&{{end}} tok != fflib.{{$element}}{{end}} { -+ return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for {{.Name}}", tok)) -+ } -+ } -+ ` -+ -+ type handleFallback struct { -+ Name string -+ Typ reflect.Type -+ Kind reflect.Kind -+ } -+ -+ var handleFallbackTxt = ` -+ { -+ /* Falling back. type={{printf "%v" .Typ}} kind={{printf "%v" .Kind}} */ -+ tbuf, err := fs.CaptureField(tok) -+ if err != nil { -+ return fs.WrapErr(err) -+ } -+ -+ err = json.Unmarshal(tbuf, &{{.Name}}) -+ if err != nil { -+ return fs.WrapErr(err) -+ } -+ } -+ ` -+ -+ type handleString struct { -+ IC *Inception -+ Name string -+ Typ reflect.Type -+ TakeAddr bool -+ Quoted bool -+ } -+ -+ var handleStringTxt = ` -+ { -+ {{$ic := .IC}} -+ -+ {{getAllowTokens .Typ.Name "FFTok_string" "FFTok_null"}} -+ if tok == fflib.FFTok_null { -+ {{if eq .TakeAddr true}} -+ {{.Name}} = nil -+ {{end}} -+ } else { -+ {{if eq .TakeAddr true}} -+ var tval {{getType $ic .Name .Typ}} -+ outBuf := fs.Output.Bytes() -+ {{unquoteField .Quoted}} -+ tval = {{getType $ic .Name .Typ}}(string(outBuf)) -+ {{.Name}} = &tval -+ {{else}} -+ outBuf := fs.Output.Bytes() -+ {{unquoteField .Quoted}} -+ {{.Name}} = {{getType $ic .Name .Typ}}(string(outBuf)) -+ {{end}} -+ } -+ } -+ ` -+ -+ type handleObject struct { -+ IC *Inception -+ Name string -+ Typ reflect.Type -+ Ptr reflect.Kind -+ TakeAddr bool -+ } -+ -+ var handleObjectTxt = ` -+ { -+ {{$ic := .IC}} -+ {{getAllowTokens .Typ.Name "FFTok_left_bracket" "FFTok_null"}} -+ if tok == fflib.FFTok_null { -+ {{.Name}} = nil -+ } else { -+ -+ {{if eq .TakeAddr true}} -+ {{if eq .Typ.Elem.Kind .Ptr }} -+ {{if eq .Typ.Key.Kind .Ptr }} -+ var tval = make(map[*{{getType $ic .Name .Typ.Key.Elem}}]*{{getType $ic .Name .Typ.Elem.Elem}}, 0) -+ {{else}} -+ var tval = make(map[{{getType $ic .Name .Typ.Key}}]*{{getType $ic .Name .Typ.Elem.Elem}}, 0) -+ {{end}} -+ {{else}} -+ {{if eq .Typ.Key.Kind .Ptr }} -+ var tval = make(map[*{{getType $ic .Name .Typ.Key.Elem}}]{{getType $ic .Name .Typ.Elem}}, 0) -+ {{else}} -+ var tval = make(map[{{getType $ic .Name .Typ.Key}}]{{getType $ic .Name .Typ.Elem}}, 0) -+ {{end}} -+ {{end}} -+ {{else}} -+ {{if eq .Typ.Elem.Kind .Ptr }} -+ {{if eq .Typ.Key.Kind .Ptr }} -+ {{.Name}} = make(map[*{{getType $ic .Name .Typ.Key.Elem}}]*{{getType $ic .Name .Typ.Elem.Elem}}, 0) -+ {{else}} -+ {{.Name}} = make(map[{{getType $ic .Name .Typ.Key}}]*{{getType $ic .Name .Typ.Elem.Elem}}, 0) -+ {{end}} -+ {{else}} -+ {{if eq .Typ.Key.Kind .Ptr }} -+ {{.Name}} = make(map[*{{getType $ic .Name .Typ.Key.Elem}}]{{getType $ic .Name .Typ.Elem}}, 0) -+ {{else}} -+ {{.Name}} = make(map[{{getType $ic .Name .Typ.Key}}]{{getType $ic .Name .Typ.Elem}}, 0) -+ {{end}} -+ {{end}} -+ {{end}} -+ -+ wantVal := true -+ -+ for { -+ {{$keyPtr := false}} -+ {{if eq .Typ.Key.Kind .Ptr }} -+ {{$keyPtr := true}} -+ var k *{{getType $ic .Name .Typ.Key.Elem}} -+ {{else}} -+ var k {{getType $ic .Name .Typ.Key}} -+ {{end}} -+ -+ {{$valPtr := false}} -+ {{$tmpVar := getTmpVarFor .Name}} -+ {{if eq .Typ.Elem.Kind .Ptr }} -+ {{$valPtr := true}} -+ var {{$tmpVar}} *{{getType $ic .Name .Typ.Elem.Elem}} -+ {{else}} -+ var {{$tmpVar}} {{getType $ic .Name .Typ.Elem}} -+ {{end}} -+ -+ tok = fs.Scan() -+ if tok == fflib.FFTok_error { -+ goto tokerror -+ } -+ if tok == fflib.FFTok_right_bracket { -+ break -+ } -+ -+ if tok == fflib.FFTok_comma { -+ if wantVal == true { -+ // TODO(pquerna): this isn't an ideal error message, this handles -+ // things like [,,,] as an array value. -+ return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok)) -+ } -+ continue -+ } else { -+ wantVal = true -+ } -+ -+ {{handleField .IC "k" .Typ.Key $keyPtr false}} -+ -+ // Expect ':' after key -+ tok = fs.Scan() -+ if tok != fflib.FFTok_colon { -+ return fs.WrapErr(fmt.Errorf("wanted colon token, but got token: %v", tok)) -+ } -+ -+ tok = fs.Scan() -+ {{handleField .IC $tmpVar .Typ.Elem $valPtr false}} -+ -+ {{if eq .TakeAddr true}} -+ tval[k] = {{$tmpVar}} -+ {{else}} -+ {{.Name}}[k] = {{$tmpVar}} -+ {{end}} -+ wantVal = false -+ } -+ -+ {{if eq .TakeAddr true}} -+ {{.Name}} = &tval -+ {{end}} -+ } -+ } -+ ` -+ -+ type handleArray struct { -+ IC *Inception -+ Name string -+ Typ reflect.Type -+ Ptr reflect.Kind -+ UseReflectToSet bool -+ IsPtr bool -+ } -+ -+ var handleArrayTxt = ` -+ { -+ {{$ic := .IC}} -+ {{getAllowTokens .Typ.Name "FFTok_left_brace" "FFTok_null"}} -+ {{if eq .Typ.Elem.Kind .Ptr}} -+ {{.Name}} = [{{.Typ.Len}}]*{{getType $ic .Name .Typ.Elem.Elem}}{} -+ {{else}} -+ {{.Name}} = [{{.Typ.Len}}]{{getType $ic .Name .Typ.Elem}}{} -+ {{end}} -+ if tok != fflib.FFTok_null { -+ wantVal := true -+ -+ idx := 0 -+ for { -+ {{$ptr := false}} -+ {{$tmpVar := getTmpVarFor .Name}} -+ {{if eq .Typ.Elem.Kind .Ptr }} -+ {{$ptr := true}} -+ var {{$tmpVar}} *{{getType $ic .Name .Typ.Elem.Elem}} -+ {{else}} -+ var {{$tmpVar}} {{getType $ic .Name .Typ.Elem}} -+ {{end}} -+ -+ tok = fs.Scan() -+ if tok == fflib.FFTok_error { -+ goto tokerror -+ } -+ if tok == fflib.FFTok_right_brace { -+ break -+ } -+ -+ if tok == fflib.FFTok_comma { -+ if wantVal == true { -+ // TODO(pquerna): this isn't an ideal error message, this handles -+ // things like [,,,] as an array value. -+ return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok)) -+ } -+ continue -+ } else { -+ wantVal = true -+ } -+ -+ {{handleField .IC $tmpVar .Typ.Elem $ptr false}} -+ -+ // Standard json.Unmarshal ignores elements out of array bounds, -+ // that what we do as well. -+ if idx < {{.Typ.Len}} { -+ {{.Name}}[idx] = {{$tmpVar}} -+ idx++ -+ } -+ -+ wantVal = false -+ } -+ } -+ } -+ ` -+ -+ var handleSliceTxt = ` -+ { -+ {{$ic := .IC}} -+ {{getAllowTokens .Typ.Name "FFTok_left_brace" "FFTok_null"}} -+ if tok == fflib.FFTok_null { -+ {{.Name}} = nil -+ } else { -+ {{if eq .Typ.Elem.Kind .Ptr }} -+ {{if eq .IsPtr true}} -+ {{.Name}} = &[]*{{getType $ic .Name .Typ.Elem.Elem}}{} -+ {{else}} -+ {{.Name}} = []*{{getType $ic .Name .Typ.Elem.Elem}}{} -+ {{end}} -+ {{else}} -+ {{if eq .IsPtr true}} -+ {{.Name}} = &[]{{getType $ic .Name .Typ.Elem}}{} -+ {{else}} -+ {{.Name}} = []{{getType $ic .Name .Typ.Elem}}{} -+ {{end}} -+ {{end}} -+ -+ wantVal := true -+ -+ for { -+ {{$ptr := false}} -+ {{$tmpVar := getTmpVarFor .Name}} -+ {{if eq .Typ.Elem.Kind .Ptr }} -+ {{$ptr := true}} -+ var {{$tmpVar}} *{{getType $ic .Name .Typ.Elem.Elem}} -+ {{else}} -+ var {{$tmpVar}} {{getType $ic .Name .Typ.Elem}} -+ {{end}} -+ -+ tok = fs.Scan() -+ if tok == fflib.FFTok_error { -+ goto tokerror -+ } -+ if tok == fflib.FFTok_right_brace { -+ break -+ } -+ -+ if tok == fflib.FFTok_comma { -+ if wantVal == true { -+ // TODO(pquerna): this isn't an ideal error message, this handles -+ // things like [,,,] as an array value. -+ return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok)) -+ } -+ continue -+ } else { -+ wantVal = true -+ } -+ -+ {{handleField .IC $tmpVar .Typ.Elem $ptr false}} -+ {{if eq .IsPtr true}} -+ *{{.Name}} = append(*{{.Name}}, {{$tmpVar}}) -+ {{else}} -+ {{.Name}} = append({{.Name}}, {{$tmpVar}}) -+ {{end}} -+ wantVal = false -+ } -+ } -+ } -+ ` -+ -+ var handleByteSliceTxt = ` -+ { -+ {{getAllowTokens .Typ.Name "FFTok_string" "FFTok_null"}} -+ if tok == fflib.FFTok_null { -+ {{.Name}} = nil -+ } else { -+ b := make([]byte, base64.StdEncoding.DecodedLen(fs.Output.Len())) -+ n, err := base64.StdEncoding.Decode(b, fs.Output.Bytes()) -+ if err != nil { -+ return fs.WrapErr(err) -+ } -+ {{if eq .UseReflectToSet true}} -+ v := reflect.ValueOf(&{{.Name}}).Elem() -+ v.SetBytes(b[0:n]) -+ {{else}} -+ {{.Name}} = append([]byte(), b[0:n]...) -+ {{end}} -+ } -+ } -+ ` -+ -+ type handleBool struct { -+ Name string -+ Typ reflect.Type -+ TakeAddr bool -+ } -+ -+ var handleBoolTxt = ` -+ { -+ if tok == fflib.FFTok_null { -+ {{if eq .TakeAddr true}} -+ {{.Name}} = nil -+ {{end}} -+ } else { -+ tmpb := fs.Output.Bytes() -+ -+ {{if eq .TakeAddr true}} -+ var tval bool -+ {{end}} -+ -+ if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { -+ {{if eq .TakeAddr true}} -+ tval = true -+ {{else}} -+ {{.Name}} = true -+ {{end}} -+ } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { -+ {{if eq .TakeAddr true}} -+ tval = false -+ {{else}} -+ {{.Name}} = false -+ {{end}} -+ } else { -+ err = errors.New("unexpected bytes for true/false value") -+ return fs.WrapErr(err) -+ } -+ -+ {{if eq .TakeAddr true}} -+ {{.Name}} = &tval -+ {{end}} -+ } -+ } -+ ` -+ -+ type handlePtr struct { -+ IC *Inception -+ Name string -+ Typ reflect.Type -+ Quoted bool -+ } -+ -+ var handlePtrTxt = ` -+ { -+ {{$ic := .IC}} -+ -+ if tok == fflib.FFTok_null { -+ {{.Name}} = nil -+ } else { -+ if {{.Name}} == nil { -+ {{.Name}} = new({{getType $ic .Typ.Elem.Name .Typ.Elem}}) -+ } -+ -+ {{handleFieldAddr .IC .Name true .Typ.Elem false .Quoted}} -+ } -+ } -+ ` -+ -+ type header struct { -+ IC *Inception -+ SI *StructInfo -+ } -+ -+ var headerTxt = ` -+ const ( -+ ffjt{{.SI.Name}}base = iota -+ ffjt{{.SI.Name}}nosuchkey -+ {{with $si := .SI}} -+ {{range $index, $field := $si.Fields}} -+ {{if ne $field.JsonName "-"}} -+ ffjt{{$si.Name}}{{$field.Name}} -+ {{end}} -+ {{end}} -+ {{end}} -+ ) -+ -+ {{with $si := .SI}} -+ {{range $index, $field := $si.Fields}} -+ {{if ne $field.JsonName "-"}} -+ var ffjKey{{$si.Name}}{{$field.Name}} = []byte({{$field.JsonName}}) -+ {{end}} -+ {{end}} -+ {{end}} -+ -+ ` -+ -+ type ujFunc struct { -+ IC *Inception -+ SI *StructInfo -+ ValidValues []string -+ ResetFields bool -+ } -+ -+ var ujFuncTxt = ` -+ {{$si := .SI}} -+ {{$ic := .IC}} -+ -+ // UnmarshalJSON umarshall json - template of ffjson -+ func (j *{{.SI.Name}}) UnmarshalJSON(input []byte) error { -+ fs := fflib.NewFFLexer(input) -+ return j.UnmarshalJSONFFLexer(fs, fflib.FFParse_map_start) -+ } -+ -+ // UnmarshalJSONFFLexer fast json unmarshall - template ffjson -+ func (j *{{.SI.Name}}) UnmarshalJSONFFLexer(fs *fflib.FFLexer, state fflib.FFParseState) error { -+ var err error -+ currentKey := ffjt{{.SI.Name}}base -+ _ = currentKey -+ tok := fflib.FFTok_init -+ wantedTok := fflib.FFTok_init -+ -+ {{if eq .ResetFields true}} -+ {{range $index, $field := $si.Fields}} -+ var ffjSet{{$si.Name}}{{$field.Name}} = false -+ {{end}} -+ {{end}} -+ -+ mainparse: -+ for { -+ tok = fs.Scan() -+ // println(fmt.Sprintf("debug: tok: %v state: %v", tok, state)) -+ if tok == fflib.FFTok_error { -+ goto tokerror -+ } -+ -+ switch state { -+ -+ case fflib.FFParse_map_start: -+ if tok != fflib.FFTok_left_bracket { -+ wantedTok = fflib.FFTok_left_bracket -+ goto wrongtokenerror -+ } -+ state = fflib.FFParse_want_key -+ continue -+ -+ case fflib.FFParse_after_value: -+ if tok == fflib.FFTok_comma { -+ state = fflib.FFParse_want_key -+ } else if tok == fflib.FFTok_right_bracket { -+ goto done -+ } else { -+ wantedTok = fflib.FFTok_comma -+ goto wrongtokenerror -+ } -+ -+ case fflib.FFParse_want_key: -+ // json {} ended. goto exit. woo. -+ if tok == fflib.FFTok_right_bracket { -+ goto done -+ } -+ if tok != fflib.FFTok_string { -+ wantedTok = fflib.FFTok_string -+ goto wrongtokenerror -+ } -+ -+ kn := fs.Output.Bytes() -+ if len(kn) <= 0 { -+ // "" case. hrm. -+ currentKey = ffjt{{.SI.Name}}nosuchkey -+ state = fflib.FFParse_want_colon -+ goto mainparse -+ } else { -+ switch kn[0] { -+ {{range $byte, $fields := $si.FieldsByFirstByte}} -+ case '{{$byte}}': -+ {{range $index, $field := $fields}} -+ {{if ne $index 0 }}} else if {{else}}if {{end}} bytes.Equal(ffjKey{{$si.Name}}{{$field.Name}}, kn) { -+ currentKey = ffjt{{$si.Name}}{{$field.Name}} -+ state = fflib.FFParse_want_colon -+ goto mainparse -+ {{end}} } -+ {{end}} -+ } -+ {{range $index, $field := $si.ReverseFields}} -+ if {{$field.FoldFuncName}}(ffjKey{{$si.Name}}{{$field.Name}}, kn) { -+ currentKey = ffjt{{$si.Name}}{{$field.Name}} -+ state = fflib.FFParse_want_colon -+ goto mainparse -+ } -+ {{end}} -+ currentKey = ffjt{{.SI.Name}}nosuchkey -+ state = fflib.FFParse_want_colon -+ goto mainparse -+ } -+ -+ case fflib.FFParse_want_colon: -+ if tok != fflib.FFTok_colon { -+ wantedTok = fflib.FFTok_colon -+ goto wrongtokenerror -+ } -+ state = fflib.FFParse_want_value -+ continue -+ case fflib.FFParse_want_value: -+ -+ if {{range $index, $v := .ValidValues}}{{if ne $index 0 }}||{{end}}tok == fflib.{{$v}}{{end}} { -+ switch currentKey { -+ {{range $index, $field := $si.Fields}} -+ case ffjt{{$si.Name}}{{$field.Name}}: -+ goto handle_{{$field.Name}} -+ {{end}} -+ case ffjt{{$si.Name}}nosuchkey: -+ err = fs.SkipField(tok) -+ if err != nil { -+ return fs.WrapErr(err) -+ } -+ state = fflib.FFParse_after_value -+ goto mainparse -+ } -+ } else { -+ goto wantedvalue -+ } -+ } -+ } -+ {{range $index, $field := $si.Fields}} -+ handle_{{$field.Name}}: -+ {{with $fieldName := $field.Name | printf "j.%s"}} -+ {{handleField $ic $fieldName $field.Typ $field.Pointer $field.ForceString}} -+ {{if eq $.ResetFields true}} -+ ffjSet{{$si.Name}}{{$field.Name}} = true -+ {{end}} -+ state = fflib.FFParse_after_value -+ goto mainparse -+ {{end}} -+ {{end}} -+ -+ wantedvalue: -+ return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok)) -+ wrongtokenerror: -+ return fs.WrapErr(fmt.Errorf("ffjson: wanted token: %v, but got token: %v output=%s", wantedTok, tok, fs.Output.String())) -+ tokerror: -+ if fs.BigError != nil { -+ return fs.WrapErr(fs.BigError) -+ } -+ err = fs.Error.ToError() -+ if err != nil { -+ return fs.WrapErr(err) -+ } -+ panic("ffjson-generated: unreachable, please report bug.") -+ done: -+ {{if eq .ResetFields true}} -+ {{range $index, $field := $si.Fields}} -+ if !ffjSet{{$si.Name}}{{$field.Name}} { -+ {{with $fieldName := $field.Name | printf "j.%s"}} -+ {{if eq $field.Pointer true}} -+ {{$fieldName}} = nil -+ {{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Interface), 10) + `}} -+ {{$fieldName}} = nil -+ {{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Slice), 10) + `}} -+ {{$fieldName}} = nil -+ {{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Array), 10) + `}} -+ {{$fieldName}} = [{{$field.Typ.Len}}]{{getType $ic $fieldName $field.Typ.Elem}}{} -+ {{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Map), 10) + `}} -+ {{$fieldName}} = nil -+ {{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Bool), 10) + `}} -+ {{$fieldName}} = false -+ {{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.String), 10) + `}} -+ {{$fieldName}} = "" -+ {{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Struct), 10) + `}} -+ {{$fieldName}} = {{getType $ic $fieldName $field.Typ}}{} -+ {{else}} -+ {{$fieldName}} = {{getType $ic $fieldName $field.Typ}}(0) -+ {{end}} -+ {{end}} -+ } -+ {{end}} -+ {{end}} -+ return nil -+ } -+ ` -+ -+ type handleUnmarshaler struct { -+ IC *Inception -+ Name string -+ Typ reflect.Type -+ Ptr reflect.Kind -+ TakeAddr bool -+ UnmarshalJSONFFLexer bool -+ Unmarshaler bool -+ } -+ -+ var handleUnmarshalerTxt = ` -+ {{$ic := .IC}} -+ -+ {{if eq .UnmarshalJSONFFLexer true}} -+ { -+ if tok == fflib.FFTok_null { -+ {{if eq .Typ.Kind .Ptr }} -+ {{.Name}} = nil -+ {{end}} -+ {{if eq .TakeAddr true }} -+ {{.Name}} = nil -+ {{end}} -+ } else { -+ {{if eq .Typ.Kind .Ptr }} -+ if {{.Name}} == nil { -+ {{.Name}} = new({{getType $ic .Typ.Elem.Name .Typ.Elem}}) -+ } -+ {{end}} -+ {{if eq .TakeAddr true }} -+ if {{.Name}} == nil { -+ {{.Name}} = new({{getType $ic .Typ.Name .Typ}}) -+ } -+ {{end}} -+ err = {{.Name}}.UnmarshalJSONFFLexer(fs, fflib.FFParse_want_key) -+ if err != nil { -+ return err -+ } -+ } -+ state = fflib.FFParse_after_value -+ } -+ {{else}} -+ {{if eq .Unmarshaler true}} -+ { -+ if tok == fflib.FFTok_null { -+ {{if eq .TakeAddr true }} -+ {{.Name}} = nil -+ {{end}} -+ } else { -+ -+ tbuf, err := fs.CaptureField(tok) -+ if err != nil { -+ return fs.WrapErr(err) -+ } -+ -+ {{if eq .TakeAddr true }} -+ if {{.Name}} == nil { -+ {{.Name}} = new({{getType $ic .Typ.Name .Typ}}) -+ } -+ {{end}} -+ err = {{.Name}}.UnmarshalJSON(tbuf) -+ if err != nil { -+ return fs.WrapErr(err) -+ } -+ } -+ state = fflib.FFParse_after_value -+ } -+ {{end}} -+ {{end}} -+ ` -diff -c /dev/null '_vendor/github.com/pquerna/ffjson/inception/encoder.go' -Index: ./github.com/pquerna/ffjson/inception/encoder.go -*** ./github.com/pquerna/ffjson/inception/encoder.go Thu Jan 1 03:00:00 1970 ---- ./github.com/pquerna/ffjson/inception/encoder.go Thu Oct 26 14:17:41 2023 -*************** -*** 0 **** ---- 1,544 ---- -+ /** -+ * Copyright 2014 Paul Querna -+ * -+ * Licensed under the Apache License, Version 2.0 (the "License"); -+ * you may not use this file except in compliance with the License. -+ * You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, software -+ * distributed under the License is distributed on an "AS IS" BASIS, -+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ * See the License for the specific language governing permissions and -+ * limitations under the License. -+ * -+ */ -+ -+ package ffjsoninception -+ -+ import ( -+ "fmt" -+ "reflect" -+ -+ "github.com/pquerna/ffjson/shared" -+ ) -+ -+ func typeInInception(ic *Inception, typ reflect.Type, f shared.Feature) bool { -+ for _, v := range ic.objs { -+ if v.Typ == typ { -+ return v.Options.HasFeature(f) -+ } -+ if typ.Kind() == reflect.Ptr { -+ if v.Typ == typ.Elem() { -+ return v.Options.HasFeature(f) -+ } -+ } -+ } -+ -+ return false -+ } -+ -+ func getOmitEmpty(ic *Inception, sf *StructField) string { -+ ptname := "j." + sf.Name -+ if sf.Pointer { -+ ptname = "*" + ptname -+ return "if true {\n" -+ } -+ switch sf.Typ.Kind() { -+ -+ case reflect.Array, reflect.Map, reflect.Slice, reflect.String: -+ return "if len(" + ptname + ") != 0 {" + "\n" -+ -+ case reflect.Int, -+ reflect.Int8, -+ reflect.Int16, -+ reflect.Int32, -+ reflect.Int64, -+ reflect.Uint, -+ reflect.Uint8, -+ reflect.Uint16, -+ reflect.Uint32, -+ reflect.Uint64, -+ reflect.Uintptr, -+ reflect.Float32, -+ reflect.Float64: -+ return "if " + ptname + " != 0 {" + "\n" -+ -+ case reflect.Bool: -+ return "if " + ptname + " != false {" + "\n" -+ -+ case reflect.Interface, reflect.Ptr: -+ return "if " + ptname + " != nil {" + "\n" -+ -+ default: -+ // TODO(pquerna): fix types -+ return "if true {" + "\n" -+ } -+ } -+ -+ func getMapValue(ic *Inception, name string, typ reflect.Type, ptr bool, forceString bool) string { -+ var out = "" -+ -+ if typ.Key().Kind() != reflect.String { -+ out += fmt.Sprintf("/* Falling back. type=%v kind=%v */\n", typ, typ.Kind()) -+ out += ic.q.Flush() -+ out += "err = buf.Encode(" + name + ")" + "\n" -+ out += "if err != nil {" + "\n" -+ out += " return err" + "\n" -+ out += "}" + "\n" -+ return out -+ } -+ -+ var elemKind reflect.Kind -+ elemKind = typ.Elem().Kind() -+ -+ switch elemKind { -+ case reflect.String, -+ reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, -+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, -+ reflect.Float32, -+ reflect.Float64, -+ reflect.Bool: -+ -+ ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true -+ -+ out += "if " + name + " == nil {" + "\n" -+ ic.q.Write("null") -+ out += ic.q.GetQueued() -+ ic.q.DeleteLast() -+ out += "} else {" + "\n" -+ out += ic.q.WriteFlush("{ ") -+ out += " for key, value := range " + name + " {" + "\n" -+ out += " fflib.WriteJsonString(buf, key)" + "\n" -+ out += " buf.WriteString(`:`)" + "\n" -+ out += getGetInnerValue(ic, "value", typ.Elem(), false, forceString) -+ out += " buf.WriteByte(',')" + "\n" -+ out += " }" + "\n" -+ out += "buf.Rewind(1)" + "\n" -+ out += ic.q.WriteFlush("}") -+ out += "}" + "\n" -+ -+ default: -+ out += ic.q.Flush() -+ out += fmt.Sprintf("/* Falling back. type=%v kind=%v */\n", typ, typ.Kind()) -+ out += "err = buf.Encode(" + name + ")" + "\n" -+ out += "if err != nil {" + "\n" -+ out += " return err" + "\n" -+ out += "}" + "\n" -+ } -+ return out -+ } -+ -+ func getGetInnerValue(ic *Inception, name string, typ reflect.Type, ptr bool, forceString bool) string { -+ var out = "" -+ -+ // Flush if not bool or maps -+ if typ.Kind() != reflect.Bool && typ.Kind() != reflect.Map && typ.Kind() != reflect.Struct { -+ out += ic.q.Flush() -+ } -+ -+ if typ.Implements(marshalerFasterType) || -+ reflect.PtrTo(typ).Implements(marshalerFasterType) || -+ typeInInception(ic, typ, shared.MustEncoder) || -+ typ.Implements(marshalerType) || -+ reflect.PtrTo(typ).Implements(marshalerType) { -+ -+ out += ic.q.Flush() -+ out += tplStr(encodeTpl["handleMarshaler"], handleMarshaler{ -+ IC: ic, -+ Name: name, -+ Typ: typ, -+ Ptr: reflect.Ptr, -+ MarshalJSONBuf: typ.Implements(marshalerFasterType) || reflect.PtrTo(typ).Implements(marshalerFasterType) || typeInInception(ic, typ, shared.MustEncoder), -+ Marshaler: typ.Implements(marshalerType) || reflect.PtrTo(typ).Implements(marshalerType), -+ }) -+ return out -+ } -+ -+ ptname := name -+ if ptr { -+ ptname = "*" + name -+ } -+ -+ switch typ.Kind() { -+ case reflect.Int, -+ reflect.Int8, -+ reflect.Int16, -+ reflect.Int32, -+ reflect.Int64: -+ ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true -+ out += "fflib.FormatBits2(buf, uint64(" + ptname + "), 10, " + ptname + " < 0)" + "\n" -+ case reflect.Uint, -+ reflect.Uint8, -+ reflect.Uint16, -+ reflect.Uint32, -+ reflect.Uint64, -+ reflect.Uintptr: -+ ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true -+ out += "fflib.FormatBits2(buf, uint64(" + ptname + "), 10, false)" + "\n" -+ case reflect.Float32: -+ ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true -+ out += "fflib.AppendFloat(buf, float64(" + ptname + "), 'g', -1, 32)" + "\n" -+ case reflect.Float64: -+ ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true -+ out += "fflib.AppendFloat(buf, float64(" + ptname + "), 'g', -1, 64)" + "\n" -+ case reflect.Array, -+ reflect.Slice: -+ -+ // Arrays cannot be nil -+ if typ.Kind() != reflect.Array { -+ out += "if " + name + "!= nil {" + "\n" -+ } -+ // Array and slice values encode as JSON arrays, except that -+ // []byte encodes as a base64-encoded string, and a nil slice -+ // encodes as the null JSON object. -+ if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 { -+ ic.OutputImports[`"encoding/base64"`] = true -+ -+ out += "buf.WriteString(`\"`)" + "\n" -+ out += `{` + "\n" -+ out += `enc := base64.NewEncoder(base64.StdEncoding, buf)` + "\n" -+ if typ.Elem().Name() != "byte" { -+ ic.OutputImports[`"reflect"`] = true -+ out += `enc.Write(reflect.Indirect(reflect.ValueOf(` + ptname + `)).Bytes())` + "\n" -+ -+ } else { -+ out += `enc.Write(` + ptname + `)` + "\n" -+ } -+ out += `enc.Close()` + "\n" -+ out += `}` + "\n" -+ out += "buf.WriteString(`\"`)" + "\n" -+ } else { -+ out += "buf.WriteString(`[`)" + "\n" -+ out += "for i, v := range " + ptname + "{" + "\n" -+ out += "if i != 0 {" + "\n" -+ out += "buf.WriteString(`,`)" + "\n" -+ out += "}" + "\n" -+ out += getGetInnerValue(ic, "v", typ.Elem(), false, false) -+ out += "}" + "\n" -+ out += "buf.WriteString(`]`)" + "\n" -+ } -+ if typ.Kind() != reflect.Array { -+ out += "} else {" + "\n" -+ out += "buf.WriteString(`null`)" + "\n" -+ out += "}" + "\n" -+ } -+ case reflect.String: -+ // Is it a json.Number? -+ if typ.PkgPath() == "encoding/json" && typ.Name() == "Number" { -+ // Fall back to json package to rely on the valid number check. -+ // See: https://github.com/golang/go/blob/92cd6e3af9f423ab4d8ac78f24e7fd81c31a8ce6/src/encoding/json/encode.go#L550 -+ out += fmt.Sprintf("/* json.Number */\n") -+ out += "err = buf.Encode(" + name + ")" + "\n" -+ out += "if err != nil {" + "\n" -+ out += " return err" + "\n" -+ out += "}" + "\n" -+ } else { -+ ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true -+ if forceString { -+ // Forcestring on strings does double-escaping of the entire value. -+ // We create a temporary buffer, encode to that an re-encode it. -+ out += "{" + "\n" -+ out += "tmpbuf := fflib.Buffer{}" + "\n" -+ out += "tmpbuf.Grow(len(" + ptname + ") + 16)" + "\n" -+ out += "fflib.WriteJsonString(&tmpbuf, string(" + ptname + "))" + "\n" -+ out += "fflib.WriteJsonString(buf, string( tmpbuf.Bytes() " + `))` + "\n" -+ out += "}" + "\n" -+ } else { -+ out += "fflib.WriteJsonString(buf, string(" + ptname + "))" + "\n" -+ } -+ } -+ case reflect.Ptr: -+ out += "if " + name + "!= nil {" + "\n" -+ switch typ.Elem().Kind() { -+ case reflect.Struct: -+ out += getGetInnerValue(ic, name, typ.Elem(), false, false) -+ default: -+ out += getGetInnerValue(ic, "*"+name, typ.Elem(), false, false) -+ } -+ out += "} else {" + "\n" -+ out += "buf.WriteString(`null`)" + "\n" -+ out += "}" + "\n" -+ case reflect.Bool: -+ out += "if " + ptname + " {" + "\n" -+ ic.q.Write("true") -+ out += ic.q.GetQueued() -+ out += "} else {" + "\n" -+ // Delete 'true' -+ ic.q.DeleteLast() -+ out += ic.q.WriteFlush("false") -+ out += "}" + "\n" -+ case reflect.Interface: -+ out += fmt.Sprintf("/* Interface types must use runtime reflection. type=%v kind=%v */\n", typ, typ.Kind()) -+ out += "err = buf.Encode(" + name + ")" + "\n" -+ out += "if err != nil {" + "\n" -+ out += " return err" + "\n" -+ out += "}" + "\n" -+ case reflect.Map: -+ out += getMapValue(ic, ptname, typ, ptr, forceString) -+ case reflect.Struct: -+ if typ.Name() == "" { -+ ic.q.Write("{") -+ ic.q.Write(" ") -+ out += fmt.Sprintf("/* Inline struct. type=%v kind=%v */\n", typ, typ.Kind()) -+ newV := reflect.Indirect(reflect.New(typ)).Interface() -+ fields := extractFields(newV) -+ -+ // Output all fields -+ for _, field := range fields { -+ // Adjust field name -+ field.Name = name + "." + field.Name -+ out += getField(ic, field, "") -+ } -+ -+ if lastConditional(fields) { -+ out += ic.q.Flush() -+ out += `buf.Rewind(1)` + "\n" -+ } else { -+ ic.q.DeleteLast() -+ } -+ out += ic.q.WriteFlush("}") -+ } else { -+ out += fmt.Sprintf("/* Struct fall back. type=%v kind=%v */\n", typ, typ.Kind()) -+ out += ic.q.Flush() -+ if ptr { -+ out += "err = buf.Encode(" + name + ")" + "\n" -+ } else { -+ // We send pointer to avoid copying entire struct -+ out += "err = buf.Encode(&" + name + ")" + "\n" -+ } -+ out += "if err != nil {" + "\n" -+ out += " return err" + "\n" -+ out += "}" + "\n" -+ } -+ default: -+ out += fmt.Sprintf("/* Falling back. type=%v kind=%v */\n", typ, typ.Kind()) -+ out += "err = buf.Encode(" + name + ")" + "\n" -+ out += "if err != nil {" + "\n" -+ out += " return err" + "\n" -+ out += "}" + "\n" -+ } -+ -+ return out -+ } -+ -+ func getValue(ic *Inception, sf *StructField, prefix string) string { -+ closequote := false -+ if sf.ForceString { -+ switch sf.Typ.Kind() { -+ case reflect.Int, -+ reflect.Int8, -+ reflect.Int16, -+ reflect.Int32, -+ reflect.Int64, -+ reflect.Uint, -+ reflect.Uint8, -+ reflect.Uint16, -+ reflect.Uint32, -+ reflect.Uint64, -+ reflect.Uintptr, -+ reflect.Float32, -+ reflect.Float64, -+ reflect.Bool: -+ ic.q.Write(`"`) -+ closequote = true -+ } -+ } -+ out := getGetInnerValue(ic, prefix+sf.Name, sf.Typ, sf.Pointer, sf.ForceString) -+ if closequote { -+ if sf.Pointer { -+ out += ic.q.WriteFlush(`"`) -+ } else { -+ ic.q.Write(`"`) -+ } -+ } -+ -+ return out -+ } -+ -+ func p2(v uint32) uint32 { -+ v-- -+ v |= v >> 1 -+ v |= v >> 2 -+ v |= v >> 4 -+ v |= v >> 8 -+ v |= v >> 16 -+ v++ -+ return v -+ } -+ -+ func getTypeSize(t reflect.Type) uint32 { -+ switch t.Kind() { -+ case reflect.String: -+ // TODO: consider runtime analysis. -+ return 32 -+ case reflect.Array, reflect.Map, reflect.Slice: -+ // TODO: consider runtime analysis. -+ return 4 * getTypeSize(t.Elem()) -+ case reflect.Int, -+ reflect.Int8, -+ reflect.Int16, -+ reflect.Int32, -+ reflect.Uint, -+ reflect.Uint8, -+ reflect.Uint16, -+ reflect.Uint32: -+ return 8 -+ case reflect.Int64, -+ reflect.Uint64, -+ reflect.Uintptr: -+ return 16 -+ case reflect.Float32, -+ reflect.Float64: -+ return 16 -+ case reflect.Bool: -+ return 4 -+ case reflect.Ptr: -+ return getTypeSize(t.Elem()) -+ default: -+ return 16 -+ } -+ } -+ -+ func getTotalSize(si *StructInfo) uint32 { -+ rv := uint32(si.Typ.Size()) -+ for _, f := range si.Fields { -+ rv += getTypeSize(f.Typ) -+ } -+ return rv -+ } -+ -+ func getBufGrowSize(si *StructInfo) uint32 { -+ -+ // TOOD(pquerna): automatically calc a better grow size based on history -+ // of a struct. -+ return p2(getTotalSize(si)) -+ } -+ -+ func isIntish(t reflect.Type) bool { -+ if t.Kind() >= reflect.Int && t.Kind() <= reflect.Uintptr { -+ return true -+ } -+ if t.Kind() == reflect.Array || t.Kind() == reflect.Slice || t.Kind() == reflect.Ptr { -+ if t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 { -+ // base64 special case. -+ return false -+ } else { -+ return isIntish(t.Elem()) -+ } -+ } -+ return false -+ } -+ -+ func getField(ic *Inception, f *StructField, prefix string) string { -+ out := "" -+ if f.OmitEmpty { -+ out += ic.q.Flush() -+ if f.Pointer { -+ out += "if " + prefix + f.Name + " != nil {" + "\n" -+ } -+ out += getOmitEmpty(ic, f) -+ } -+ -+ if f.Pointer && !f.OmitEmpty { -+ // Pointer values encode as the value pointed to. A nil pointer encodes as the null JSON object. -+ out += "if " + prefix + f.Name + " != nil {" + "\n" -+ } -+ -+ // JsonName is already escaped and quoted. -+ // getInnervalue should flush -+ ic.q.Write(f.JsonName + ":") -+ // We save a copy in case we need it -+ t := ic.q -+ -+ out += getValue(ic, f, prefix) -+ ic.q.Write(",") -+ -+ if f.Pointer && !f.OmitEmpty { -+ out += "} else {" + "\n" -+ out += t.WriteFlush("null") -+ out += "}" + "\n" -+ } -+ -+ if f.OmitEmpty { -+ out += ic.q.Flush() -+ if f.Pointer { -+ out += "}" + "\n" -+ } -+ out += "}" + "\n" -+ } -+ return out -+ } -+ -+ // We check if the last field is conditional. -+ func lastConditional(fields []*StructField) bool { -+ if len(fields) > 0 { -+ f := fields[len(fields)-1] -+ return f.OmitEmpty -+ } -+ return false -+ } -+ -+ func CreateMarshalJSON(ic *Inception, si *StructInfo) error { -+ conditionalWrites := lastConditional(si.Fields) -+ out := "" -+ -+ out += "// MarshalJSON marshal bytes to json - template\n" -+ out += `func (j *` + si.Name + `) MarshalJSON() ([]byte, error) {` + "\n" -+ out += `var buf fflib.Buffer` + "\n" -+ -+ out += `if j == nil {` + "\n" -+ out += ` buf.WriteString("null")` + "\n" -+ out += " return buf.Bytes(), nil" + "\n" -+ out += `}` + "\n" -+ -+ out += `err := j.MarshalJSONBuf(&buf)` + "\n" -+ out += `if err != nil {` + "\n" -+ out += " return nil, err" + "\n" -+ out += `}` + "\n" -+ out += `return buf.Bytes(), nil` + "\n" -+ out += `}` + "\n" -+ -+ out += "// MarshalJSONBuf marshal buff to json - template\n" -+ out += `func (j *` + si.Name + `) MarshalJSONBuf(buf fflib.EncodingBuffer) (error) {` + "\n" -+ out += ` if j == nil {` + "\n" -+ out += ` buf.WriteString("null")` + "\n" -+ out += " return nil" + "\n" -+ out += ` }` + "\n" -+ -+ out += `var err error` + "\n" -+ out += `var obj []byte` + "\n" -+ out += `_ = obj` + "\n" -+ out += `_ = err` + "\n" -+ -+ ic.q.Write("{") -+ -+ // The extra space is inserted here. -+ // If nothing is written to the field this will be deleted -+ // instead of the last comma. -+ if conditionalWrites || len(si.Fields) == 0 { -+ ic.q.Write(" ") -+ } -+ -+ for _, f := range si.Fields { -+ out += getField(ic, f, "j.") -+ } -+ -+ // Handling the last comma is tricky. -+ // If the last field has omitempty, conditionalWrites is set. -+ // If something has been written, we delete the last comma, -+ // by backing up the buffer, otherwise it will delete a space. -+ if conditionalWrites { -+ out += ic.q.Flush() -+ out += `buf.Rewind(1)` + "\n" -+ } else { -+ ic.q.DeleteLast() -+ } -+ -+ out += ic.q.WriteFlush("}") -+ out += `return nil` + "\n" -+ out += `}` + "\n" -+ ic.OutputFuncs = append(ic.OutputFuncs, out) -+ return nil -+ } -diff -c /dev/null '_vendor/github.com/pquerna/ffjson/inception/encoder_tpl.go' -Index: ./github.com/pquerna/ffjson/inception/encoder_tpl.go -*** ./github.com/pquerna/ffjson/inception/encoder_tpl.go Thu Jan 1 03:00:00 1970 ---- ./github.com/pquerna/ffjson/inception/encoder_tpl.go Thu Oct 26 14:17:41 2023 -*************** -*** 0 **** ---- 1,73 ---- -+ /** -+ * Copyright 2014 Paul Querna -+ * -+ * Licensed under the Apache License, Version 2.0 (the "License"); -+ * you may not use this file except in compliance with the License. -+ * You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, software -+ * distributed under the License is distributed on an "AS IS" BASIS, -+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ * See the License for the specific language governing permissions and -+ * limitations under the License. -+ * -+ */ -+ -+ package ffjsoninception -+ -+ import ( -+ "reflect" -+ "text/template" -+ ) -+ -+ var encodeTpl map[string]*template.Template -+ -+ func init() { -+ encodeTpl = make(map[string]*template.Template) -+ -+ funcs := map[string]string{ -+ "handleMarshaler": handleMarshalerTxt, -+ } -+ tplFuncs := template.FuncMap{} -+ -+ for k, v := range funcs { -+ encodeTpl[k] = template.Must(template.New(k).Funcs(tplFuncs).Parse(v)) -+ } -+ } -+ -+ type handleMarshaler struct { -+ IC *Inception -+ Name string -+ Typ reflect.Type -+ Ptr reflect.Kind -+ MarshalJSONBuf bool -+ Marshaler bool -+ } -+ -+ var handleMarshalerTxt = ` -+ { -+ {{if eq .Typ.Kind .Ptr}} -+ if {{.Name}} == nil { -+ buf.WriteString("null") -+ } else { -+ {{end}} -+ -+ {{if eq .MarshalJSONBuf true}} -+ err = {{.Name}}.MarshalJSONBuf(buf) -+ if err != nil { -+ return err -+ } -+ {{else if eq .Marshaler true}} -+ obj, err = {{.Name}}.MarshalJSON() -+ if err != nil { -+ return err -+ } -+ buf.Write(obj) -+ {{end}} -+ {{if eq .Typ.Kind .Ptr}} -+ } -+ {{end}} -+ } -+ ` -diff -c /dev/null '_vendor/github.com/pquerna/ffjson/inception/inception.go' -Index: ./github.com/pquerna/ffjson/inception/inception.go -*** ./github.com/pquerna/ffjson/inception/inception.go Thu Jan 1 03:00:00 1970 ---- ./github.com/pquerna/ffjson/inception/inception.go Thu Oct 26 14:17:41 2023 -*************** -*** 0 **** ---- 1,160 ---- -+ /** -+ * Copyright 2014 Paul Querna -+ * -+ * Licensed under the Apache License, Version 2.0 (the "License"); -+ * you may not use this file except in compliance with the License. -+ * You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, software -+ * distributed under the License is distributed on an "AS IS" BASIS, -+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ * See the License for the specific language governing permissions and -+ * limitations under the License. -+ * -+ */ -+ -+ package ffjsoninception -+ -+ import ( -+ "errors" -+ "fmt" -+ "github.com/pquerna/ffjson/shared" -+ "io/ioutil" -+ "os" -+ "reflect" -+ "sort" -+ ) -+ -+ type Inception struct { -+ objs []*StructInfo -+ InputPath string -+ OutputPath string -+ PackageName string -+ PackagePath string -+ OutputImports map[string]bool -+ OutputFuncs []string -+ q ConditionalWrite -+ ResetFields bool -+ } -+ -+ func NewInception(inputPath string, packageName string, outputPath string, resetFields bool) *Inception { -+ return &Inception{ -+ objs: make([]*StructInfo, 0), -+ InputPath: inputPath, -+ OutputPath: outputPath, -+ PackageName: packageName, -+ OutputFuncs: make([]string, 0), -+ OutputImports: make(map[string]bool), -+ ResetFields: resetFields, -+ } -+ } -+ -+ func (i *Inception) AddMany(objs []shared.InceptionType) { -+ for _, obj := range objs { -+ i.Add(obj) -+ } -+ } -+ -+ func (i *Inception) Add(obj shared.InceptionType) { -+ i.objs = append(i.objs, NewStructInfo(obj)) -+ i.PackagePath = i.objs[0].Typ.PkgPath() -+ } -+ -+ func (i *Inception) wantUnmarshal(si *StructInfo) bool { -+ if si.Options.SkipDecoder { -+ return false -+ } -+ typ := si.Typ -+ umlx := typ.Implements(unmarshalFasterType) || reflect.PtrTo(typ).Implements(unmarshalFasterType) -+ umlstd := typ.Implements(unmarshalerType) || reflect.PtrTo(typ).Implements(unmarshalerType) -+ if umlstd && !umlx { -+ // structure has UnmarshalJSON, but not our faster version -- skip it. -+ return false -+ } -+ return true -+ } -+ -+ func (i *Inception) wantMarshal(si *StructInfo) bool { -+ if si.Options.SkipEncoder { -+ return false -+ } -+ typ := si.Typ -+ mlx := typ.Implements(marshalerFasterType) || reflect.PtrTo(typ).Implements(marshalerFasterType) -+ mlstd := typ.Implements(marshalerType) || reflect.PtrTo(typ).Implements(marshalerType) -+ if mlstd && !mlx { -+ // structure has MarshalJSON, but not our faster version -- skip it. -+ return false -+ } -+ return true -+ } -+ -+ type sortedStructs []*StructInfo -+ -+ func (p sortedStructs) Len() int { return len(p) } -+ func (p sortedStructs) Less(i, j int) bool { return p[i].Name < p[j].Name } -+ func (p sortedStructs) Swap(i, j int) { p[i], p[j] = p[j], p[i] } -+ func (p sortedStructs) Sort() { sort.Sort(p) } -+ -+ func (i *Inception) generateCode() error { -+ // We sort the structs by name, so output if predictable. -+ sorted := sortedStructs(i.objs) -+ sorted.Sort() -+ -+ for _, si := range sorted { -+ if i.wantMarshal(si) { -+ err := CreateMarshalJSON(i, si) -+ if err != nil { -+ return err -+ } -+ } -+ -+ if i.wantUnmarshal(si) { -+ err := CreateUnmarshalJSON(i, si) -+ if err != nil { -+ return err -+ } -+ } -+ } -+ return nil -+ } -+ -+ func (i *Inception) handleError(err error) { -+ fmt.Fprintf(os.Stderr, "Error: %s:\n\n", err) -+ os.Exit(1) -+ } -+ -+ func (i *Inception) Execute() { -+ if len(os.Args) != 1 { -+ i.handleError(errors.New(fmt.Sprintf("Internal ffjson error: inception executable takes no args: %v", os.Args))) -+ return -+ } -+ -+ err := i.generateCode() -+ if err != nil { -+ i.handleError(err) -+ return -+ } -+ -+ data, err := RenderTemplate(i) -+ if err != nil { -+ i.handleError(err) -+ return -+ } -+ -+ stat, err := os.Stat(i.InputPath) -+ -+ if err != nil { -+ i.handleError(err) -+ return -+ } -+ -+ err = ioutil.WriteFile(i.OutputPath, data, stat.Mode()) -+ -+ if err != nil { -+ i.handleError(err) -+ return -+ } -+ -+ } -diff -c /dev/null '_vendor/github.com/pquerna/ffjson/inception/reflect.go' -Index: ./github.com/pquerna/ffjson/inception/reflect.go -*** ./github.com/pquerna/ffjson/inception/reflect.go Thu Jan 1 03:00:00 1970 ---- ./github.com/pquerna/ffjson/inception/reflect.go Thu Oct 26 14:17:41 2023 -*************** -*** 0 **** ---- 1,290 ---- -+ /** -+ * Copyright 2014 Paul Querna -+ * -+ * Licensed under the Apache License, Version 2.0 (the "License"); -+ * you may not use this file except in compliance with the License. -+ * You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, software -+ * distributed under the License is distributed on an "AS IS" BASIS, -+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ * See the License for the specific language governing permissions and -+ * limitations under the License. -+ * -+ */ -+ -+ package ffjsoninception -+ -+ import ( -+ fflib "github.com/pquerna/ffjson/fflib/v1" -+ "github.com/pquerna/ffjson/shared" -+ -+ "bytes" -+ "encoding/json" -+ "reflect" -+ "unicode/utf8" -+ ) -+ -+ type StructField struct { -+ Name string -+ JsonName string -+ FoldFuncName string -+ Typ reflect.Type -+ OmitEmpty bool -+ ForceString bool -+ HasMarshalJSON bool -+ HasUnmarshalJSON bool -+ Pointer bool -+ Tagged bool -+ } -+ -+ type FieldByJsonName []*StructField -+ -+ func (a FieldByJsonName) Len() int { return len(a) } -+ func (a FieldByJsonName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -+ func (a FieldByJsonName) Less(i, j int) bool { return a[i].JsonName < a[j].JsonName } -+ -+ type StructInfo struct { -+ Name string -+ Obj interface{} -+ Typ reflect.Type -+ Fields []*StructField -+ Options shared.StructOptions -+ } -+ -+ func NewStructInfo(obj shared.InceptionType) *StructInfo { -+ t := reflect.TypeOf(obj.Obj) -+ return &StructInfo{ -+ Obj: obj.Obj, -+ Name: t.Name(), -+ Typ: t, -+ Fields: extractFields(obj.Obj), -+ Options: obj.Options, -+ } -+ } -+ -+ func (si *StructInfo) FieldsByFirstByte() map[string][]*StructField { -+ rv := make(map[string][]*StructField) -+ for _, f := range si.Fields { -+ b := string(f.JsonName[1]) -+ rv[b] = append(rv[b], f) -+ } -+ return rv -+ } -+ -+ func (si *StructInfo) ReverseFields() []*StructField { -+ var i int -+ rv := make([]*StructField, 0) -+ for i = len(si.Fields) - 1; i >= 0; i-- { -+ rv = append(rv, si.Fields[i]) -+ } -+ return rv -+ } -+ -+ const ( -+ caseMask = ^byte(0x20) // Mask to ignore case in ASCII. -+ ) -+ -+ func foldFunc(key []byte) string { -+ nonLetter := false -+ special := false // special letter -+ for _, b := range key { -+ if b >= utf8.RuneSelf { -+ return "bytes.EqualFold" -+ } -+ upper := b & caseMask -+ if upper < 'A' || upper > 'Z' { -+ nonLetter = true -+ } else if upper == 'K' || upper == 'S' { -+ // See above for why these letters are special. -+ special = true -+ } -+ } -+ if special { -+ return "fflib.EqualFoldRight" -+ } -+ if nonLetter { -+ return "fflib.AsciiEqualFold" -+ } -+ return "fflib.SimpleLetterEqualFold" -+ } -+ -+ type MarshalerFaster interface { -+ MarshalJSONBuf(buf fflib.EncodingBuffer) error -+ } -+ -+ type UnmarshalFaster interface { -+ UnmarshalJSONFFLexer(l *fflib.FFLexer, state fflib.FFParseState) error -+ } -+ -+ var marshalerType = reflect.TypeOf(new(json.Marshaler)).Elem() -+ var marshalerFasterType = reflect.TypeOf(new(MarshalerFaster)).Elem() -+ var unmarshalerType = reflect.TypeOf(new(json.Unmarshaler)).Elem() -+ var unmarshalFasterType = reflect.TypeOf(new(UnmarshalFaster)).Elem() -+ -+ // extractFields returns a list of fields that JSON should recognize for the given type. -+ // The algorithm is breadth-first search over the set of structs to include - the top struct -+ // and then any reachable anonymous structs. -+ func extractFields(obj interface{}) []*StructField { -+ t := reflect.TypeOf(obj) -+ // Anonymous fields to explore at the current level and the next. -+ current := []StructField{} -+ next := []StructField{{Typ: t}} -+ -+ // Count of queued names for current level and the next. -+ count := map[reflect.Type]int{} -+ nextCount := map[reflect.Type]int{} -+ -+ // Types already visited at an earlier level. -+ visited := map[reflect.Type]bool{} -+ -+ // Fields found. -+ var fields []*StructField -+ -+ for len(next) > 0 { -+ current, next = next, current[:0] -+ count, nextCount = nextCount, map[reflect.Type]int{} -+ -+ for _, f := range current { -+ if visited[f.Typ] { -+ continue -+ } -+ visited[f.Typ] = true -+ -+ // Scan f.typ for fields to include. -+ for i := 0; i < f.Typ.NumField(); i++ { -+ sf := f.Typ.Field(i) -+ if sf.PkgPath != "" { // unexported -+ continue -+ } -+ tag := sf.Tag.Get("json") -+ if tag == "-" { -+ continue -+ } -+ name, opts := parseTag(tag) -+ if !isValidTag(name) { -+ name = "" -+ } -+ -+ ft := sf.Type -+ ptr := false -+ if ft.Kind() == reflect.Ptr { -+ ptr = true -+ } -+ -+ if ft.Name() == "" && ft.Kind() == reflect.Ptr { -+ // Follow pointer. -+ ft = ft.Elem() -+ } -+ -+ // Record found field and index sequence. -+ if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { -+ tagged := name != "" -+ if name == "" { -+ name = sf.Name -+ } -+ -+ var buf bytes.Buffer -+ fflib.WriteJsonString(&buf, name) -+ -+ field := &StructField{ -+ Name: sf.Name, -+ JsonName: string(buf.Bytes()), -+ FoldFuncName: foldFunc([]byte(name)), -+ Typ: ft, -+ HasMarshalJSON: ft.Implements(marshalerType), -+ HasUnmarshalJSON: ft.Implements(unmarshalerType), -+ OmitEmpty: opts.Contains("omitempty"), -+ ForceString: opts.Contains("string"), -+ Pointer: ptr, -+ Tagged: tagged, -+ } -+ -+ fields = append(fields, field) -+ -+ if count[f.Typ] > 1 { -+ // If there were multiple instances, add a second, -+ // so that the annihilation code will see a duplicate. -+ // It only cares about the distinction between 1 or 2, -+ // so don't bother generating any more copies. -+ fields = append(fields, fields[len(fields)-1]) -+ } -+ continue -+ } -+ -+ // Record new anonymous struct to explore in next round. -+ nextCount[ft]++ -+ if nextCount[ft] == 1 { -+ next = append(next, StructField{ -+ Name: ft.Name(), -+ Typ: ft, -+ }) -+ } -+ } -+ } -+ } -+ -+ // Delete all fields that are hidden by the Go rules for embedded fields, -+ // except that fields with JSON tags are promoted. -+ -+ // The fields are sorted in primary order of name, secondary order -+ // of field index length. Loop over names; for each name, delete -+ // hidden fields by choosing the one dominant field that survives. -+ out := fields[:0] -+ for advance, i := 0, 0; i < len(fields); i += advance { -+ // One iteration per name. -+ // Find the sequence of fields with the name of this first field. -+ fi := fields[i] -+ name := fi.JsonName -+ for advance = 1; i+advance < len(fields); advance++ { -+ fj := fields[i+advance] -+ if fj.JsonName != name { -+ break -+ } -+ } -+ if advance == 1 { // Only one field with this name -+ out = append(out, fi) -+ continue -+ } -+ dominant, ok := dominantField(fields[i : i+advance]) -+ if ok { -+ out = append(out, dominant) -+ } -+ } -+ -+ fields = out -+ -+ return fields -+ } -+ -+ // dominantField looks through the fields, all of which are known to -+ // have the same name, to find the single field that dominates the -+ // others using Go's embedding rules, modified by the presence of -+ // JSON tags. If there are multiple top-level fields, the boolean -+ // will be false: This condition is an error in Go and we skip all -+ // the fields. -+ func dominantField(fields []*StructField) (*StructField, bool) { -+ tagged := -1 // Index of first tagged field. -+ for i, f := range fields { -+ if f.Tagged { -+ if tagged >= 0 { -+ // Multiple tagged fields at the same level: conflict. -+ // Return no field. -+ return nil, false -+ } -+ tagged = i -+ } -+ } -+ if tagged >= 0 { -+ return fields[tagged], true -+ } -+ // All remaining fields have the same length. If there's more than one, -+ // we have a conflict (two fields named "X" at the same level) and we -+ // return no field. -+ if len(fields) > 1 { -+ return nil, false -+ } -+ return fields[0], true -+ } -diff -c /dev/null '_vendor/github.com/pquerna/ffjson/inception/tags.go' -Index: ./github.com/pquerna/ffjson/inception/tags.go -*** ./github.com/pquerna/ffjson/inception/tags.go Thu Jan 1 03:00:00 1970 ---- ./github.com/pquerna/ffjson/inception/tags.go Thu Oct 26 14:17:41 2023 -*************** -*** 0 **** ---- 1,79 ---- -+ /** -+ * Copyright 2014 Paul Querna -+ * -+ * Licensed under the Apache License, Version 2.0 (the "License"); -+ * you may not use this file except in compliance with the License. -+ * You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, software -+ * distributed under the License is distributed on an "AS IS" BASIS, -+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ * See the License for the specific language governing permissions and -+ * limitations under the License. -+ * -+ */ -+ -+ package ffjsoninception -+ -+ import ( -+ "strings" -+ "unicode" -+ ) -+ -+ // from: http://golang.org/src/pkg/encoding/json/tags.go -+ -+ // tagOptions is the string following a comma in a struct field's "json" -+ // tag, or the empty string. It does not include the leading comma. -+ type tagOptions string -+ -+ // parseTag splits a struct field's json tag into its name and -+ // comma-separated options. -+ func parseTag(tag string) (string, tagOptions) { -+ if idx := strings.Index(tag, ","); idx != -1 { -+ return tag[:idx], tagOptions(tag[idx+1:]) -+ } -+ return tag, tagOptions("") -+ } -+ -+ // Contains reports whether a comma-separated list of options -+ // contains a particular substr flag. substr must be surrounded by a -+ // string boundary or commas. -+ func (o tagOptions) Contains(optionName string) bool { -+ if len(o) == 0 { -+ return false -+ } -+ s := string(o) -+ for s != "" { -+ var next string -+ i := strings.Index(s, ",") -+ if i >= 0 { -+ s, next = s[:i], s[i+1:] -+ } -+ if s == optionName { -+ return true -+ } -+ s = next -+ } -+ return false -+ } -+ -+ func isValidTag(s string) bool { -+ if s == "" { -+ return false -+ } -+ for _, c := range s { -+ switch { -+ case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c): -+ // Backslash and quote chars are reserved, but -+ // otherwise any punctuation chars are allowed -+ // in a tag name. -+ default: -+ if !unicode.IsLetter(c) && !unicode.IsDigit(c) { -+ return false -+ } -+ } -+ } -+ return true -+ } -diff -c /dev/null '_vendor/github.com/pquerna/ffjson/inception/template.go' -Index: ./github.com/pquerna/ffjson/inception/template.go -*** ./github.com/pquerna/ffjson/inception/template.go Thu Jan 1 03:00:00 1970 ---- ./github.com/pquerna/ffjson/inception/template.go Thu Oct 26 14:17:41 2023 -*************** -*** 0 **** ---- 1,60 ---- -+ /** -+ * Copyright 2014 Paul Querna -+ * -+ * Licensed under the Apache License, Version 2.0 (the "License"); -+ * you may not use this file except in compliance with the License. -+ * You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, software -+ * distributed under the License is distributed on an "AS IS" BASIS, -+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ * See the License for the specific language governing permissions and -+ * limitations under the License. -+ * -+ */ -+ -+ package ffjsoninception -+ -+ import ( -+ "bytes" -+ "go/format" -+ "text/template" -+ ) -+ -+ const ffjsonTemplate = ` -+ // Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT. -+ // source: {{.InputPath}} -+ -+ package {{.PackageName}} -+ -+ import ( -+ {{range $k, $v := .OutputImports}}{{$k}} -+ {{end}} -+ ) -+ -+ {{range .OutputFuncs}} -+ {{.}} -+ {{end}} -+ -+ ` -+ -+ func RenderTemplate(ic *Inception) ([]byte, error) { -+ t := template.Must(template.New("ffjson.go").Parse(ffjsonTemplate)) -+ buf := new(bytes.Buffer) -+ err := t.Execute(buf, ic) -+ if err != nil { -+ return nil, err -+ } -+ return format.Source(buf.Bytes()) -+ } -+ -+ func tplStr(t *template.Template, data interface{}) string { -+ buf := bytes.Buffer{} -+ err := t.Execute(&buf, data) -+ if err != nil { -+ panic(err) -+ } -+ return buf.String() -+ } -diff -c /dev/null '_vendor/github.com/pquerna/ffjson/inception/writerstack.go' -Index: ./github.com/pquerna/ffjson/inception/writerstack.go -*** ./github.com/pquerna/ffjson/inception/writerstack.go Thu Jan 1 03:00:00 1970 ---- ./github.com/pquerna/ffjson/inception/writerstack.go Thu Oct 26 14:17:41 2023 -*************** -*** 0 **** ---- 1,65 ---- -+ package ffjsoninception -+ -+ import "strings" -+ -+ // ConditionalWrite is a stack containing a number of pending writes -+ type ConditionalWrite struct { -+ Queued []string -+ } -+ -+ // Write will add a string to be written -+ func (w *ConditionalWrite) Write(s string) { -+ w.Queued = append(w.Queued, s) -+ } -+ -+ // DeleteLast will delete the last added write -+ func (w *ConditionalWrite) DeleteLast() { -+ if len(w.Queued) == 0 { -+ return -+ } -+ w.Queued = w.Queued[:len(w.Queued)-1] -+ } -+ -+ // Last will return the last added write -+ func (w *ConditionalWrite) Last() string { -+ if len(w.Queued) == 0 { -+ return "" -+ } -+ return w.Queued[len(w.Queued)-1] -+ } -+ -+ // Flush will return all queued writes, and return -+ // "" (empty string) in nothing has been queued -+ // "buf.WriteByte('" + byte + "')" + '\n' if one bute has been queued. -+ // "buf.WriteString(`" + string + "`)" + "\n" if more than one byte has been queued. -+ func (w *ConditionalWrite) Flush() string { -+ combined := strings.Join(w.Queued, "") -+ if len(combined) == 0 { -+ return "" -+ } -+ -+ w.Queued = nil -+ if len(combined) == 1 { -+ return "buf.WriteByte('" + combined + "')" + "\n" -+ } -+ return "buf.WriteString(`" + combined + "`)" + "\n" -+ } -+ -+ func (w *ConditionalWrite) FlushTo(out string) string { -+ out += w.Flush() -+ return out -+ } -+ -+ // WriteFlush will add a string and return the Flush result for the queue -+ func (w *ConditionalWrite) WriteFlush(s string) string { -+ w.Write(s) -+ return w.Flush() -+ } -+ -+ // GetQueued will return the current queued content without flushing. -+ func (w *ConditionalWrite) GetQueued() string { -+ t := w.Queued -+ s := w.Flush() -+ w.Queued = t -+ return s -+ } -diff -c /dev/null '_vendor/github.com/pquerna/ffjson/shared/options.go' -Index: ./github.com/pquerna/ffjson/shared/options.go -*** ./github.com/pquerna/ffjson/shared/options.go Thu Jan 1 03:00:00 1970 ---- ./github.com/pquerna/ffjson/shared/options.go Thu Oct 26 14:17:41 2023 -*************** -*** 0 **** ---- 1,51 ---- -+ /** -+ * Copyright 2014 Paul Querna, Klaus Post -+ * -+ * Licensed under the Apache License, Version 2.0 (the "License"); -+ * you may not use this file except in compliance with the License. -+ * You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, software -+ * distributed under the License is distributed on an "AS IS" BASIS, -+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ * See the License for the specific language governing permissions and -+ * limitations under the License. -+ * -+ */ -+ -+ package shared -+ -+ type StructOptions struct { -+ SkipDecoder bool -+ SkipEncoder bool -+ } -+ -+ type InceptionType struct { -+ Obj interface{} -+ Options StructOptions -+ } -+ type Feature int -+ -+ const ( -+ Nothing Feature = 0 -+ MustDecoder = 1 << 1 -+ MustEncoder = 1 << 2 -+ MustEncDec = MustDecoder | MustEncoder -+ ) -+ -+ func (i InceptionType) HasFeature(f Feature) bool { -+ return i.HasFeature(f) -+ } -+ -+ func (s StructOptions) HasFeature(f Feature) bool { -+ hasNeeded := true -+ if f&MustDecoder != 0 && s.SkipDecoder { -+ hasNeeded = false -+ } -+ if f&MustEncoder != 0 && s.SkipEncoder { -+ hasNeeded = false -+ } -+ return hasNeeded -+ } -diff -c 'vendor/github.com/prometheus/client_golang/prometheus/go_collector.go' '_vendor/github.com/prometheus/client_golang/prometheus/go_collector.go' -Index: ./github.com/prometheus/client_golang/prometheus/go_collector.go -*** ./github.com/prometheus/client_golang/prometheus/go_collector.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/client_golang/prometheus/go_collector.go Thu Oct 26 15:21:07 2023 -*************** -*** 15,36 **** - - import ( - "runtime" -- "runtime/debug" -- "time" - ) - - func goRuntimeMemStats() memStatsMetrics { - return memStatsMetrics{ - { - desc: NewDesc( -- memstatNamespace("alloc_bytes"), -- "Number of bytes allocated and still in use.", -- nil, nil, -- ), -- eval: func(ms *runtime.MemStats) float64 { return float64(ms.Alloc) }, -- valType: GaugeValue, -- }, { -- desc: NewDesc( - memstatNamespace("alloc_bytes_total"), - "Total number of bytes allocated, even if freed.", - nil, nil, ---- 15,26 ---- -*************** -*** 47,60 **** - valType: GaugeValue, - }, { - desc: NewDesc( -- memstatNamespace("lookups_total"), -- "Total number of pointer lookups.", -- nil, nil, -- ), -- eval: func(ms *runtime.MemStats) float64 { return float64(ms.Lookups) }, -- valType: CounterValue, -- }, { -- desc: NewDesc( - memstatNamespace("mallocs_total"), - "Total number of mallocs.", - nil, nil, ---- 37,42 ---- -*************** -*** 71,84 **** - valType: CounterValue, - }, { - desc: NewDesc( -- memstatNamespace("heap_alloc_bytes"), -- "Number of heap bytes allocated and still in use.", -- nil, nil, -- ), -- eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapAlloc) }, -- valType: GaugeValue, -- }, { -- desc: NewDesc( - memstatNamespace("heap_sys_bytes"), - "Number of heap bytes obtained from system.", - nil, nil, ---- 53,58 ---- -*************** -*** 111,202 **** - valType: GaugeValue, - }, { - desc: NewDesc( -- memstatNamespace("heap_objects"), -- "Number of allocated objects.", -- nil, nil, -- ), -- eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapObjects) }, -- valType: GaugeValue, -- }, { -- desc: NewDesc( -- memstatNamespace("stack_inuse_bytes"), -- "Number of bytes in use by the stack allocator.", -- nil, nil, -- ), -- eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackInuse) }, -- valType: GaugeValue, -- }, { -- desc: NewDesc( -- memstatNamespace("stack_sys_bytes"), -- "Number of bytes obtained from system for stack allocator.", -- nil, nil, -- ), -- eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackSys) }, -- valType: GaugeValue, -- }, { -- desc: NewDesc( -- memstatNamespace("mspan_inuse_bytes"), -- "Number of bytes in use by mspan structures.", -- nil, nil, -- ), -- eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanInuse) }, -- valType: GaugeValue, -- }, { -- desc: NewDesc( -- memstatNamespace("mspan_sys_bytes"), -- "Number of bytes used for mspan structures obtained from system.", -- nil, nil, -- ), -- eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanSys) }, -- valType: GaugeValue, -- }, { -- desc: NewDesc( -- memstatNamespace("mcache_inuse_bytes"), -- "Number of bytes in use by mcache structures.", -- nil, nil, -- ), -- eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheInuse) }, -- valType: GaugeValue, -- }, { -- desc: NewDesc( -- memstatNamespace("mcache_sys_bytes"), -- "Number of bytes used for mcache structures obtained from system.", -- nil, nil, -- ), -- eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheSys) }, -- valType: GaugeValue, -- }, { -- desc: NewDesc( -- memstatNamespace("buck_hash_sys_bytes"), -- "Number of bytes used by the profiling bucket hash table.", -- nil, nil, -- ), -- eval: func(ms *runtime.MemStats) float64 { return float64(ms.BuckHashSys) }, -- valType: GaugeValue, -- }, { -- desc: NewDesc( - memstatNamespace("gc_sys_bytes"), - "Number of bytes used for garbage collection system metadata.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.GCSys) }, - valType: GaugeValue, -- }, { -- desc: NewDesc( -- memstatNamespace("other_sys_bytes"), -- "Number of bytes used for other system allocations.", -- nil, nil, -- ), -- eval: func(ms *runtime.MemStats) float64 { return float64(ms.OtherSys) }, -- valType: GaugeValue, -- }, { -- desc: NewDesc( -- memstatNamespace("next_gc_bytes"), -- "Number of heap bytes when next garbage collection will take place.", -- nil, nil, -- ), -- eval: func(ms *runtime.MemStats) float64 { return float64(ms.NextGC) }, -- valType: GaugeValue, - }, - } - } ---- 85,96 ---- -*************** -*** 245,266 **** - - // Collect returns the current state of all metrics of the collector. - func (c *baseGoCollector) Collect(ch chan<- Metric) { -! ch <- MustNewConstMetric(c.goroutinesDesc, GaugeValue, float64(runtime.NumGoroutine())) -! n, _ := runtime.ThreadCreateProfile(nil) -! ch <- MustNewConstMetric(c.threadsDesc, GaugeValue, float64(n)) -! -! var stats debug.GCStats -! stats.PauseQuantiles = make([]time.Duration, 5) -! debug.ReadGCStats(&stats) -! -! quantiles := make(map[float64]float64) -! for idx, pq := range stats.PauseQuantiles[1:] { -! quantiles[float64(idx+1)/float64(len(stats.PauseQuantiles)-1)] = pq.Seconds() -! } -! quantiles[0.0] = stats.PauseQuantiles[0].Seconds() -! ch <- MustNewConstSummary(c.gcDesc, uint64(stats.NumGC), stats.PauseTotal.Seconds(), quantiles) -! ch <- MustNewConstMetric(c.gcLastTimeDesc, GaugeValue, float64(stats.LastGC.UnixNano())/1e9) -! ch <- MustNewConstMetric(c.goInfoDesc, GaugeValue, 1) - } - - func memstatNamespace(s string) string { ---- 139,145 ---- - - // Collect returns the current state of all metrics of the collector. - func (c *baseGoCollector) Collect(ch chan<- Metric) { -! - } - - func memstatNamespace(s string) string { -diff -c 'vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go' '_vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go' -Index: ./github.com/prometheus/client_golang/prometheus/go_collector_latest.go -*** ./github.com/prometheus/client_golang/prometheus/go_collector_latest.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/client_golang/prometheus/go_collector_latest.go Thu Oct 26 15:21:07 2023 -*************** -*** 20,26 **** - "math" - "runtime" - "runtime/metrics" -- "strings" - "sync" - - //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. ---- 20,25 ---- -*************** -*** 78,90 **** - - func bestEffortLookupRM(lookup []string) []metrics.Description { - ret := make([]metrics.Description, 0, len(lookup)) -- for _, rm := range metrics.All() { -- for _, m := range lookup { -- if m == rm.Name { -- ret = append(ret, rm) -- } -- } -- } - return ret - } - ---- 77,82 ---- -*************** -*** 156,183 **** - // The API guarantees that the buckets are always fixed for the lifetime - // of the process. - var histograms []metrics.Sample -- for _, d := range descriptions { -- if d.Kind == metrics.KindFloat64Histogram { -- histograms = append(histograms, metrics.Sample{Name: d.Name}) -- } -- } - - if len(histograms) > 0 { - metrics.Read(histograms) - } - -- bucketsMap := make(map[string][]float64) -- for i := range histograms { -- bucketsMap[histograms[i].Name] = histograms[i].Value.Float64Histogram().Buckets -- } -- - // Generate a Desc and ValueType for each runtime/metrics metric. - metricSet := make([]collectorMetric, 0, len(descriptions)) - sampleBuf := make([]metrics.Sample, 0, len(descriptions)) - sampleMap := make(map[string]*metrics.Sample, len(descriptions)) - for i := range descriptions { - d := &descriptions[i] -! namespace, subsystem, name, ok := internal.RuntimeMetricsToProm(d) - if !ok { - // Just ignore this metric; we can't do anything with it here. - // If a user decides to use the latest version of Go, we don't want ---- 148,165 ---- - // The API guarantees that the buckets are always fixed for the lifetime - // of the process. - var histograms []metrics.Sample - - if len(histograms) > 0 { - metrics.Read(histograms) - } - - // Generate a Desc and ValueType for each runtime/metrics metric. - metricSet := make([]collectorMetric, 0, len(descriptions)) - sampleBuf := make([]metrics.Sample, 0, len(descriptions)) - sampleMap := make(map[string]*metrics.Sample, len(descriptions)) - for i := range descriptions { - d := &descriptions[i] -! _, _, _, ok := internal.RuntimeMetricsToProm(d) - if !ok { - // Just ignore this metric; we can't do anything with it here. - // If a user decides to use the latest version of Go, we don't want -*************** -*** 187,224 **** - - // Set up sample buffer for reading, and a map - // for quick lookup of sample values. -- sampleBuf = append(sampleBuf, metrics.Sample{Name: d.Name}) -- sampleMap[d.Name] = &sampleBuf[len(sampleBuf)-1] - - var m collectorMetric -- if d.Kind == metrics.KindFloat64Histogram { -- _, hasSum := rmExactSumMap[d.Name] -- unit := d.Name[strings.IndexRune(d.Name, ':')+1:] -- m = newBatchHistogram( -- NewDesc( -- BuildFQName(namespace, subsystem, name), -- d.Description, -- nil, -- nil, -- ), -- internal.RuntimeMetricsBucketsForUnit(bucketsMap[d.Name], unit), -- hasSum, -- ) -- } else if d.Cumulative { -- m = NewCounter(CounterOpts{ -- Namespace: namespace, -- Subsystem: subsystem, -- Name: name, -- Help: d.Description, -- }) -- } else { -- m = NewGauge(GaugeOpts{ -- Namespace: namespace, -- Subsystem: subsystem, -- Name: name, -- Help: d.Description, -- }) -- } - metricSet = append(metricSet, m) - } - ---- 169,176 ---- -*************** -*** 273,303 **** - } - - if c.opt.isEnabled(goRuntimeMetricsCollection) { -- // Collect all our metrics from rmSampleBuf. -- for i, sample := range c.rmSampleBuf { -- // N.B. switch on concrete type because it's significantly more efficient -- // than checking for the Counter and Gauge interface implementations. In -- // this case, we control all the types here. -- switch m := c.rmMetrics[i].(type) { -- case *counter: -- // Guard against decreases. This should never happen, but a failure -- // to do so will result in a panic, which is a harsh consequence for -- // a metrics collection bug. -- v0, v1 := m.get(), unwrapScalarRMValue(sample.Value) -- if v1 > v0 { -- m.Add(unwrapScalarRMValue(sample.Value) - m.get()) -- } -- m.Collect(ch) -- case *gauge: -- m.Set(unwrapScalarRMValue(sample.Value)) -- m.Collect(ch) -- case *batchHistogram: -- m.update(sample.Value.Float64Histogram(), c.exactSumFor(sample.Name)) -- m.Collect(ch) -- default: -- panic("unexpected metric type") -- } -- } - } - - // ms is a dummy MemStats that we populate ourselves so that we can ---- 225,230 ---- -*************** -*** 315,338 **** - // to be scalar and returns the equivalent float64 value. Panics if the - // value is not scalar. - func unwrapScalarRMValue(v metrics.Value) float64 { -! switch v.Kind() { -! case metrics.KindUint64: -! return float64(v.Uint64()) -! case metrics.KindFloat64: -! return v.Float64() -! case metrics.KindBad: -! // Unsupported metric. -! // -! // This should never happen because we always populate our metric -! // set from the runtime/metrics package. -! panic("unexpected unsupported metric") -! default: -! // Unsupported metric kind. -! // -! // This should never happen because we check for this during initialization -! // and flag and filter metrics whose kinds we don't understand. -! panic("unexpected unsupported metric kind") -! } - } - - var rmExactSumMap = map[string]string{ ---- 242,248 ---- - // to be scalar and returns the equivalent float64 value. Panics if the - // value is not scalar. - func unwrapScalarRMValue(v metrics.Value) float64 { -! return 0 - } - - var rmExactSumMap = map[string]string{ -*************** -*** 351,368 **** - if !ok { - return 0 - } -! s, ok := c.rmSampleMap[sumName] - if !ok { - return 0 - } -! return unwrapScalarRMValue(s.Value) - } - - func memStatsFromRM(ms *runtime.MemStats, rm map[string]*metrics.Sample) { - lookupOrZero := func(name string) uint64 { -- if s, ok := rm[name]; ok { -- return s.Value.Uint64() -- } - return 0 - } - ---- 261,275 ---- - if !ok { - return 0 - } -! _, ok = c.rmSampleMap[sumName] - if !ok { - return 0 - } -! return 0 - } - - func memStatsFromRM(ms *runtime.MemStats, rm map[string]*metrics.Sample) { - lookupOrZero := func(name string) uint64 { - return 0 - } - -*************** -*** 378,408 **** - - ms.TotalAlloc = lookupOrZero(goGCHeapAllocsBytes) - ms.Sys = lookupOrZero(goMemoryClassesTotalBytes) -- ms.Lookups = 0 // Already always zero. -- ms.HeapAlloc = lookupOrZero(goMemoryClassesHeapObjectsBytes) -- ms.Alloc = ms.HeapAlloc -- ms.HeapInuse = ms.HeapAlloc + lookupOrZero(goMemoryClassesHeapUnusedBytes) - ms.HeapReleased = lookupOrZero(goMemoryClassesHeapReleasedBytes) - ms.HeapIdle = ms.HeapReleased + lookupOrZero(goMemoryClassesHeapFreeBytes) - ms.HeapSys = ms.HeapInuse + ms.HeapIdle -- ms.HeapObjects = lookupOrZero(goGCHeapObjects) -- ms.StackInuse = lookupOrZero(goMemoryClassesHeapStacksBytes) -- ms.StackSys = ms.StackInuse + lookupOrZero(goMemoryClassesOSStacksBytes) -- ms.MSpanInuse = lookupOrZero(goMemoryClassesMetadataMSpanInuseBytes) -- ms.MSpanSys = ms.MSpanInuse + lookupOrZero(goMemoryClassesMetadataMSPanFreeBytes) -- ms.MCacheInuse = lookupOrZero(goMemoryClassesMetadataMCacheInuseBytes) -- ms.MCacheSys = ms.MCacheInuse + lookupOrZero(goMemoryClassesMetadataMCacheFreeBytes) -- ms.BuckHashSys = lookupOrZero(goMemoryClassesProfilingBucketsBytes) - ms.GCSys = lookupOrZero(goMemoryClassesMetadataOtherBytes) -- ms.OtherSys = lookupOrZero(goMemoryClassesOtherBytes) -- ms.NextGC = lookupOrZero(goGCHeapGoalBytes) - - // N.B. GCCPUFraction is intentionally omitted. This metric is not useful, - // and often misleading due to the fact that it's an average over the lifetime - // of the process. - // See https://github.com/prometheus/client_golang/issues/842#issuecomment-861812034 - // for more details. -- ms.GCCPUFraction = 0 - } - - // batchHistogram is a mutable histogram that is updated ---- 285,300 ---- -*************** -*** 454,479 **** - // sum must be provided if the batchHistogram was created to have an exact sum. - // h.buckets must be a strict subset of his.Buckets. - func (h *batchHistogram) update(his *metrics.Float64Histogram, sum float64) { -- counts, buckets := his.Counts, his.Buckets -- -- h.mu.Lock() -- defer h.mu.Unlock() -- -- // Clear buckets. -- for i := range h.counts { -- h.counts[i] = 0 -- } -- // Copy and reduce buckets. -- var j int -- for i, count := range counts { -- h.counts[j] += count -- if buckets[i+1] == h.buckets[j+1] { -- j++ -- } -- } -- if h.hasSum { -- h.sum = sum -- } - } - - func (h *batchHistogram) Desc() *Desc { ---- 346,351 ---- -diff -c 'vendor/github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go' '_vendor/github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go' -Index: ./github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go -*** ./github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go Thu Oct 26 15:21:07 2023 -*************** -*** 18,28 **** - - import ( - "math" -- "path" - "runtime/metrics" -- "strings" -- -- "github.com/prometheus/common/model" - ) - - // RuntimeMetricsToProm produces a Prometheus metric name from a runtime/metrics ---- 18,24 ---- -*************** -*** 37,80 **** - // character set. This is theoretically possible, but should never happen in practice. - // Still, don't rely on it. - func RuntimeMetricsToProm(d *metrics.Description) (string, string, string, bool) { -! namespace := "go" -! -! comp := strings.SplitN(d.Name, ":", 2) -! key := comp[0] -! unit := comp[1] -! -! // The last path element in the key is the name, -! // the rest is the subsystem. -! subsystem := path.Dir(key[1:] /* remove leading / */) -! name := path.Base(key) -! -! // subsystem is translated by replacing all / and - with _. -! subsystem = strings.ReplaceAll(subsystem, "/", "_") -! subsystem = strings.ReplaceAll(subsystem, "-", "_") -! -! // unit is translated assuming that the unit contains no -! // non-ASCII characters. -! unit = strings.ReplaceAll(unit, "-", "_") -! unit = strings.ReplaceAll(unit, "*", "_") -! unit = strings.ReplaceAll(unit, "/", "_per_") -! -! // name has - replaced with _ and is concatenated with the unit and -! // other data. -! name = strings.ReplaceAll(name, "-", "_") -! name = name + "_" + unit -! if d.Cumulative && d.Kind != metrics.KindFloat64Histogram { -! name = name + "_total" -! } -! -! valid := model.IsValidMetricName(model.LabelValue(namespace + "_" + subsystem + "_" + name)) -! switch d.Kind { -! case metrics.KindUint64: -! case metrics.KindFloat64: -! case metrics.KindFloat64Histogram: -! default: -! valid = false -! } -! return namespace, subsystem, name, valid - } - - // RuntimeMetricsBucketsForUnit takes a set of buckets obtained for a runtime/metrics histogram ---- 33,39 ---- - // character set. This is theoretically possible, but should never happen in practice. - // Still, don't rely on it. - func RuntimeMetricsToProm(d *metrics.Description) (string, string, string, bool) { -! return "fake", "fake", "fake", true - } - - // RuntimeMetricsBucketsForUnit takes a set of buckets obtained for a runtime/metrics histogram -diff -c 'vendor/github.com/prometheus/client_golang/prometheus/registry.go' '_vendor/github.com/prometheus/client_golang/prometheus/registry.go' -Index: ./github.com/prometheus/client_golang/prometheus/registry.go -*** ./github.com/prometheus/client_golang/prometheus/registry.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/client_golang/prometheus/registry.go Thu Oct 26 15:21:07 2023 -*************** -*** 59,66 **** - ) - - func init() { -! MustRegister(NewProcessCollector(ProcessCollectorOpts{})) -! MustRegister(NewGoCollector()) - } - - // NewRegistry creates a new vanilla Registry without any Collectors ---- 59,66 ---- - ) - - func init() { -! /*MustRegister(NewProcessCollector(ProcessCollectorOpts{})) -! MustRegister(NewGoCollector())*/ - } - - // NewRegistry creates a new vanilla Registry without any Collectors -*************** -*** 575,584 **** - return err - } - -! if err := os.Chmod(tmp.Name(), 0644); err != nil { -! return err -! } -! return os.Rename(tmp.Name(), filename) - } - - // processMetric is an internal helper method only used by the Gather method. ---- 575,581 ---- - return err - } - -! return nil - } - - // processMetric is an internal helper method only used by the Gather method. -diff -c 'vendor/github.com/prometheus/client_golang/prometheus/testutil/promlint/promlint.go' '_vendor/github.com/prometheus/client_golang/prometheus/testutil/promlint/promlint.go' -Index: ./github.com/prometheus/client_golang/prometheus/testutil/promlint/promlint.go -*** ./github.com/prometheus/client_golang/prometheus/testutil/promlint/promlint.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/client_golang/prometheus/testutil/promlint/promlint.go Mon Mar 11 17:47:29 2024 -*************** -*** 17,23 **** - import ( - "fmt" - "io" -! "regexp" - "sort" - "strings" - ---- 17,23 ---- - import ( - "fmt" - "io" -! "wasm_parts/regexp" - "sort" - "strings" - -diff -c 'vendor/github.com/prometheus/common/model/labels.go' '_vendor/github.com/prometheus/common/model/labels.go' -Index: ./github.com/prometheus/common/model/labels.go -*** ./github.com/prometheus/common/model/labels.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/common/model/labels.go Mon Mar 11 17:56:25 2024 -*************** -*** 16,22 **** - import ( - "encoding/json" - "fmt" -! "regexp" - "strings" - "unicode/utf8" - ) ---- 16,22 ---- - import ( - "encoding/json" - "fmt" -! "wasm_parts/regexp" - "strings" - "unicode/utf8" - ) -*************** -*** 91,97 **** - // LabelNameRE is a regular expression matching valid label names. Note that the - // IsValid method of LabelName performs the same check but faster than a match - // with this regular expression. -! var LabelNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$") - - // A LabelName is a key for a LabelSet or Metric. It has a value associated - // therewith. ---- 91,97 ---- - // LabelNameRE is a regular expression matching valid label names. Note that the - // IsValid method of LabelName performs the same check but faster than a match - // with this regular expression. -! var LabelNameRE = func() *regexp.Regexp { return regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$") } - - // A LabelName is a key for a LabelSet or Metric. It has a value associated - // therewith. -diff -c 'vendor/github.com/prometheus/common/model/metric.go' '_vendor/github.com/prometheus/common/model/metric.go' -Index: ./github.com/prometheus/common/model/metric.go -*** ./github.com/prometheus/common/model/metric.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/common/model/metric.go Mon Mar 11 17:57:53 2024 -*************** -*** 15,21 **** - - import ( - "fmt" -! "regexp" - "sort" - "strings" - ) ---- 15,21 ---- - - import ( - "fmt" -! "wasm_parts/regexp" - "sort" - "strings" - ) -*************** -*** 24,30 **** - // MetricNameRE is a regular expression matching valid metric - // names. Note that the IsValidMetricName function performs the same - // check but faster than a match with this regular expression. -! MetricNameRE = regexp.MustCompile(`^[a-zA-Z_:][a-zA-Z0-9_:]*$`) - ) - - // A Metric is similar to a LabelSet, but the key difference is that a Metric is ---- 24,30 ---- - // MetricNameRE is a regular expression matching valid metric - // names. Note that the IsValidMetricName function performs the same - // check but faster than a match with this regular expression. -! MetricNameRE = func() *regexp.Regexp { return regexp.MustCompile(`^[a-zA-Z_:][a-zA-Z0-9_:]*$`) } - ) - - // A Metric is similar to a LabelSet, but the key difference is that a Metric is -diff -c 'vendor/github.com/prometheus/common/model/silence.go' '_vendor/github.com/prometheus/common/model/silence.go' -Index: ./github.com/prometheus/common/model/silence.go -*** ./github.com/prometheus/common/model/silence.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/common/model/silence.go Mon Mar 11 17:47:29 2024 -*************** -*** 16,22 **** - import ( - "encoding/json" - "fmt" -! "regexp" - "time" - ) - ---- 16,22 ---- - import ( - "encoding/json" - "fmt" -! "wasm_parts/regexp" - "time" - ) - -diff -c 'vendor/github.com/prometheus/common/model/time.go' '_vendor/github.com/prometheus/common/model/time.go' -Index: ./github.com/prometheus/common/model/time.go -*** ./github.com/prometheus/common/model/time.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/common/model/time.go Mon Mar 11 17:52:45 2024 -*************** -*** 18,24 **** - "errors" - "fmt" - "math" -! "regexp" - "strconv" - "strings" - "time" ---- 18,24 ---- - "errors" - "fmt" - "math" -! "wasm_parts/regexp" - "strconv" - "strings" - "time" -*************** -*** 183,193 **** - return "duration" - } - -! var durationRE = regexp.MustCompile("^(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?$") - - // ParseDuration parses a string into a time.Duration, assuming that a year - // always has 365d, a week always has 7d, and a day always has 24h. - func ParseDuration(durationStr string) (Duration, error) { - switch durationStr { - case "0": - // Allow 0 without a unit. ---- 183,194 ---- - return "duration" - } - -! - - // ParseDuration parses a string into a time.Duration, assuming that a year - // always has 365d, a week always has 7d, and a day always has 24h. - func ParseDuration(durationStr string) (Duration, error) { -+ var durationRE = regexp.MustCompile("^(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?$") - switch durationStr { - case "0": - // Allow 0 without a unit. -diff -c 'vendor/github.com/prometheus/procfs/cpuinfo.go' '_vendor/github.com/prometheus/procfs/cpuinfo.go' -Index: ./github.com/prometheus/procfs/cpuinfo.go -*** ./github.com/prometheus/procfs/cpuinfo.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/procfs/cpuinfo.go Mon Mar 11 17:47:29 2024 -*************** -*** 20,26 **** - "bytes" - "errors" - "fmt" -! "regexp" - "strconv" - "strings" - ---- 20,26 ---- - "bytes" - "errors" - "fmt" -! "wasm_parts/regexp" - "strconv" - "strings" - -diff -c 'vendor/github.com/prometheus/procfs/mdstat.go' '_vendor/github.com/prometheus/procfs/mdstat.go' -Index: ./github.com/prometheus/procfs/mdstat.go -*** ./github.com/prometheus/procfs/mdstat.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/procfs/mdstat.go Mon Mar 11 17:47:29 2024 -*************** -*** 16,22 **** - import ( - "fmt" - "io/ioutil" -! "regexp" - "strconv" - "strings" - ) ---- 16,22 ---- - import ( - "fmt" - "io/ioutil" -! "wasm_parts/regexp" - "strconv" - "strings" - ) -diff -c 'vendor/github.com/prometheus/procfs/proc_fdinfo.go' '_vendor/github.com/prometheus/procfs/proc_fdinfo.go' -Index: ./github.com/prometheus/procfs/proc_fdinfo.go -*** ./github.com/prometheus/procfs/proc_fdinfo.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/procfs/proc_fdinfo.go Mon Mar 11 17:47:29 2024 -*************** -*** 17,23 **** - "bufio" - "bytes" - "fmt" -! "regexp" - - "github.com/prometheus/procfs/internal/util" - ) ---- 17,23 ---- - "bufio" - "bytes" - "fmt" -! "wasm_parts/regexp" - - "github.com/prometheus/procfs/internal/util" - ) -diff -c 'vendor/github.com/prometheus/procfs/proc_limits.go' '_vendor/github.com/prometheus/procfs/proc_limits.go' -Index: ./github.com/prometheus/procfs/proc_limits.go -*** ./github.com/prometheus/procfs/proc_limits.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/procfs/proc_limits.go Mon Mar 11 17:47:20 2024 -*************** -*** 17,23 **** - "bufio" - "fmt" - "os" -! "regexp" - "strconv" - ) - ---- 17,23 ---- - "bufio" - "fmt" - "os" -! "wasm_parts/regexp" - "strconv" - ) - -diff -c 'vendor/github.com/prometheus/procfs/proc_smaps.go' '_vendor/github.com/prometheus/procfs/proc_smaps.go' -Index: ./github.com/prometheus/procfs/proc_smaps.go -*** ./github.com/prometheus/procfs/proc_smaps.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/procfs/proc_smaps.go Mon Mar 11 17:47:29 2024 -*************** -*** 20,26 **** - "errors" - "fmt" - "os" -! "regexp" - "strconv" - "strings" - ---- 20,26 ---- - "errors" - "fmt" - "os" -! "wasm_parts/regexp" - "strconv" - "strings" - -diff -c 'vendor/github.com/prometheus/procfs/proc_stat.go' '_vendor/github.com/prometheus/procfs/proc_stat.go' -Index: ./github.com/prometheus/procfs/proc_stat.go -*** ./github.com/prometheus/procfs/proc_stat.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/procfs/proc_stat.go Thu Oct 26 15:21:07 2023 -*************** -*** 16,22 **** - import ( - "bytes" - "fmt" -- "os" - - "github.com/prometheus/procfs/internal/fs" - "github.com/prometheus/procfs/internal/util" ---- 16,21 ---- -*************** -*** 198,204 **** - - // ResidentMemory returns the resident memory size in bytes. - func (s ProcStat) ResidentMemory() int { -! return s.RSS * os.Getpagesize() - } - - // StartTime returns the unix timestamp of the process in seconds. ---- 197,203 ---- - - // ResidentMemory returns the resident memory size in bytes. - func (s ProcStat) ResidentMemory() int { -! return s.RSS * 65536 - } - - // StartTime returns the unix timestamp of the process in seconds. -diff -c 'vendor/github.com/prometheus/procfs/schedstat.go' '_vendor/github.com/prometheus/procfs/schedstat.go' -Index: ./github.com/prometheus/procfs/schedstat.go -*** ./github.com/prometheus/procfs/schedstat.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/procfs/schedstat.go Mon Mar 11 17:47:29 2024 -*************** -*** 17,23 **** - "bufio" - "errors" - "os" -! "regexp" - "strconv" - ) - ---- 17,23 ---- - "bufio" - "errors" - "os" -! "wasm_parts/regexp" - "strconv" - ) - -diff -c 'vendor/github.com/prometheus/procfs/slab.go' '_vendor/github.com/prometheus/procfs/slab.go' -Index: ./github.com/prometheus/procfs/slab.go -*** ./github.com/prometheus/procfs/slab.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/procfs/slab.go Mon Mar 11 17:47:29 2024 -*************** -*** 17,23 **** - "bufio" - "bytes" - "fmt" -! "regexp" - "strconv" - "strings" - ---- 17,23 ---- - "bufio" - "bytes" - "fmt" -! "wasm_parts/regexp" - "strconv" - "strings" - -diff -c 'vendor/github.com/prometheus/procfs/zoneinfo.go' '_vendor/github.com/prometheus/procfs/zoneinfo.go' -Index: ./github.com/prometheus/procfs/zoneinfo.go -*** ./github.com/prometheus/procfs/zoneinfo.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/procfs/zoneinfo.go Mon Mar 11 17:47:29 2024 -*************** -*** 19,25 **** - "bytes" - "fmt" - "io/ioutil" -! "regexp" - "strings" - - "github.com/prometheus/procfs/internal/util" ---- 19,25 ---- - "bytes" - "fmt" - "io/ioutil" -! "wasm_parts/regexp" - "strings" - - "github.com/prometheus/procfs/internal/util" -diff -c 'vendor/github.com/prometheus/prometheus/discovery/registry.go' '_vendor/github.com/prometheus/prometheus/discovery/registry.go' -Index: ./github.com/prometheus/prometheus/discovery/registry.go -*** ./github.com/prometheus/prometheus/discovery/registry.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/prometheus/discovery/registry.go Thu Oct 26 15:21:07 2023 -*************** -*** 17,23 **** - "errors" - "fmt" - "reflect" -- "sort" - "strconv" - "strings" - "sync" ---- 17,22 ---- -*************** -*** 58,64 **** - } - - func registerConfig(yamlKey string, elemType reflect.Type, config Config) { -! name := config.Name() - if _, ok := configNames[name]; ok { - panic(fmt.Sprintf("discovery: Config named %q is already registered", name)) - } ---- 57,63 ---- - } - - func registerConfig(yamlKey string, elemType reflect.Type, config Config) { -! /*name := config.Name() - if _, ok := configNames[name]; ok { - panic(fmt.Sprintf("discovery: Config named %q is already registered", name)) - } -*************** -*** 77,83 **** - Name: fieldName, - Type: reflect.SliceOf(elemType), - Tag: reflect.StructTag(`yaml:"` + yamlKey + `,omitempty"`), -! } - } - - func getConfigType(out reflect.Type) reflect.Type { ---- 76,82 ---- - Name: fieldName, - Type: reflect.SliceOf(elemType), - Tag: reflect.StructTag(`yaml:"` + yamlKey + `,omitempty"`), -! }*/ - } - - func getConfigType(out reflect.Type) reflect.Type { -diff -c 'vendor/github.com/prometheus/prometheus/promql/engine.go' '_vendor/github.com/prometheus/prometheus/promql/engine.go' -Index: ./github.com/prometheus/prometheus/promql/engine.go -*** ./github.com/prometheus/prometheus/promql/engine.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/prometheus/promql/engine.go Mon Mar 11 11:26:55 2024 -*************** -*** 20,26 **** - "errors" - "fmt" - "math" -- "reflect" - "runtime" - "sort" - "strconv" ---- 20,25 ---- -*************** -*** 30,47 **** - "github.com/go-kit/log" - "github.com/go-kit/log/level" - "github.com/grafana/regexp" -- "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/model" -- "go.opentelemetry.io/otel" -- "go.opentelemetry.io/otel/attribute" -- "go.opentelemetry.io/otel/trace" - - "github.com/prometheus/prometheus/model/labels" - "github.com/prometheus/prometheus/model/timestamp" - "github.com/prometheus/prometheus/model/value" - "github.com/prometheus/prometheus/promql/parser" - "github.com/prometheus/prometheus/storage" -- "github.com/prometheus/prometheus/util/stats" - ) - - const ( ---- 29,41 ---- -*************** -*** 57,73 **** - minInt64 = -9223372036854775808 - ) - -- type engineMetrics struct { -- currentQueries prometheus.Gauge -- maxConcurrentQueries prometheus.Gauge -- queryLogEnabled prometheus.Gauge -- queryLogFailures prometheus.Counter -- queryQueueTime prometheus.Observer -- queryPrepareTime prometheus.Observer -- queryInnerEval prometheus.Observer -- queryResultSort prometheus.Observer -- } -- - // convertibleToInt64 returns true if v does not over-/underflow an int64. - func convertibleToInt64(v float64) bool { - return v <= maxInt64 && v >= minInt64 ---- 51,56 ---- -*************** -*** 117,124 **** - Close() - // Statement returns the parsed statement of the query. - Statement() parser.Statement -- // Stats returns statistics about the lifetime of the query. -- Stats() *stats.Statistics - // Cancel signals that a running query execution should be aborted. - Cancel() - // String returns the original query string. ---- 100,105 ---- -*************** -*** 138,147 **** - q string - // Statement of the parsed query. - stmt parser.Statement -- // Timer stats for the query execution. -- stats *stats.QueryTimers -- // Sample stats for the query execution. -- sampleStats *stats.QuerySamples - // Result matrix for reuse. - matrix Matrix - // Cancellation function for the query. ---- 119,124 ---- -*************** -*** 165,178 **** - return q.q - } - -- // Stats implements the Query interface. -- func (q *query) Stats() *stats.Statistics { -- return &stats.Statistics{ -- Timers: q.stats, -- Samples: q.sampleStats, -- } -- } -- - // Cancel implements the Query interface. - func (q *query) Cancel() { - if q.cancel != nil { ---- 142,147 ---- -*************** -*** 189,197 **** - - // Exec implements the Query interface. - func (q *query) Exec(ctx context.Context) *Result { -- if span := trace.SpanFromContext(ctx); span != nil { -- span.SetAttributes(attribute.String(queryTag, q.stmt.String())) -- } - - // Exec query. - res, warnings, err := q.ng.exec(ctx, q) ---- 158,163 ---- -*************** -*** 240,246 **** - // EngineOpts contains configuration options used when creating a new Engine. - type EngineOpts struct { - Logger log.Logger -- Reg prometheus.Registerer - MaxSamples int - Timeout time.Duration - ActiveQueryTracker QueryTracker ---- 206,211 ---- -*************** -*** 273,279 **** - // It is connected to a querier. - type Engine struct { - logger log.Logger -- metrics *engineMetrics - timeout time.Duration - maxSamplesPerQuery int - activeQueryTracker QueryTracker ---- 238,243 ---- -*************** -*** 292,344 **** - opts.Logger = log.NewNopLogger() - } - -- queryResultSummary := prometheus.NewSummaryVec(prometheus.SummaryOpts{ -- Namespace: namespace, -- Subsystem: subsystem, -- Name: "query_duration_seconds", -- Help: "Query timings", -- Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, -- }, -- []string{"slice"}, -- ) -- -- metrics := &engineMetrics{ -- currentQueries: prometheus.NewGauge(prometheus.GaugeOpts{ -- Namespace: namespace, -- Subsystem: subsystem, -- Name: "queries", -- Help: "The current number of queries being executed or waiting.", -- }), -- queryLogEnabled: prometheus.NewGauge(prometheus.GaugeOpts{ -- Namespace: namespace, -- Subsystem: subsystem, -- Name: "query_log_enabled", -- Help: "State of the query log.", -- }), -- queryLogFailures: prometheus.NewCounter(prometheus.CounterOpts{ -- Namespace: namespace, -- Subsystem: subsystem, -- Name: "query_log_failures_total", -- Help: "The number of query log failures.", -- }), -- maxConcurrentQueries: prometheus.NewGauge(prometheus.GaugeOpts{ -- Namespace: namespace, -- Subsystem: subsystem, -- Name: "queries_concurrent_max", -- Help: "The max number of concurrent queries.", -- }), -- queryQueueTime: queryResultSummary.WithLabelValues("queue_time"), -- queryPrepareTime: queryResultSummary.WithLabelValues("prepare_time"), -- queryInnerEval: queryResultSummary.WithLabelValues("inner_eval"), -- queryResultSort: queryResultSummary.WithLabelValues("result_sort"), -- } -- -- if t := opts.ActiveQueryTracker; t != nil { -- metrics.maxConcurrentQueries.Set(float64(t.GetMaxConcurrent())) -- } else { -- metrics.maxConcurrentQueries.Set(-1) -- } -- - if opts.LookbackDelta == 0 { - opts.LookbackDelta = defaultLookbackDelta - if l := opts.Logger; l != nil { ---- 256,261 ---- -*************** -*** 346,365 **** - } - } - -- if opts.Reg != nil { -- opts.Reg.MustRegister( -- metrics.currentQueries, -- metrics.maxConcurrentQueries, -- metrics.queryLogEnabled, -- metrics.queryLogFailures, -- queryResultSummary, -- ) -- } -- - return &Engine{ - timeout: opts.Timeout, - logger: opts.Logger, -- metrics: metrics, - maxSamplesPerQuery: opts.MaxSamples, - activeQueryTracker: opts.ActiveQueryTracker, - lookbackDelta: opts.LookbackDelta, ---- 263,271 ---- -*************** -*** 385,396 **** - } - - ng.queryLogger = l -- -- if l != nil { -- ng.metrics.queryLogEnabled.Set(1) -- } else { -- ng.metrics.queryLogEnabled.Set(0) -- } - } - - // NewInstantQuery returns an evaluation query for the given expression at the given time. ---- 291,296 ---- -*************** -*** 446,453 **** - qry := &query{ - stmt: es, - ng: ng, -- stats: stats.NewQueryTimers(), -- sampleStats: stats.NewQuerySamples(ng.enablePerStepStats && opts.EnablePerStepStats), - queryable: q, - } - return qry, nil ---- 346,351 ---- -*************** -*** 514,521 **** - q: "test statement", - stmt: parser.TestStmt(f), - ng: ng, -- stats: stats.NewQueryTimers(), -- sampleStats: stats.NewQuerySamples(ng.enablePerStepStats), - } - return qry - } ---- 412,417 ---- -*************** -*** 525,532 **** - // At this point per query only one EvalStmt is evaluated. Alert and record - // statements are not handled by the Engine. - func (ng *Engine) exec(ctx context.Context, q *query) (v parser.Value, ws storage.Warnings, err error) { -- ng.metrics.currentQueries.Inc() -- defer ng.metrics.currentQueries.Dec() - - ctx, cancel := context.WithTimeout(ctx, ng.timeout) - q.cancel = cancel ---- 421,426 ---- -*************** -*** 546,589 **** - if err != nil { - f = append(f, "error", err) - } -- f = append(f, "stats", stats.NewQueryStats(q.Stats())) -- if span := trace.SpanFromContext(ctx); span != nil { -- f = append(f, "spanID", span.SpanContext().SpanID()) -- } - if origin := ctx.Value(QueryOrigin{}); origin != nil { - for k, v := range origin.(map[string]interface{}) { - f = append(f, k, v) - } - } - if err := l.Log(f...); err != nil { -- ng.metrics.queryLogFailures.Inc() - level.Error(ng.logger).Log("msg", "can't log query", "err", err) - } - } - ng.queryLoggerLock.RUnlock() - }() - -- execSpanTimer, ctx := q.stats.GetSpanTimer(ctx, stats.ExecTotalTime) -- defer execSpanTimer.Finish() - -- queueSpanTimer, _ := q.stats.GetSpanTimer(ctx, stats.ExecQueueTime, ng.metrics.queryQueueTime) - // Log query in active log. The active log guarantees that we don't run over - // MaxConcurrent queries. - if ng.activeQueryTracker != nil { - queryIndex, err := ng.activeQueryTracker.Insert(ctx, q.q) - if err != nil { -- queueSpanTimer.Finish() - return nil, nil, contextErr(err, "query queue") - } - defer ng.activeQueryTracker.Delete(queryIndex) - } -- queueSpanTimer.Finish() - - // Cancel when execution is done or an error was raised. - defer q.cancel() - -- evalSpanTimer, ctx := q.stats.GetSpanTimer(ctx, stats.EvalTotalTime) -- defer evalSpanTimer.Finish() - - // The base context might already be canceled on the first iteration (e.g. during shutdown). - if err := contextDone(ctx, env); err != nil { ---- 440,471 ---- -*************** -*** 610,631 **** - - // execEvalStmt evaluates the expression of an evaluation statement for the given time range. - func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *parser.EvalStmt) (parser.Value, storage.Warnings, error) { -! prepareSpanTimer, ctxPrepare := query.stats.GetSpanTimer(ctx, stats.QueryPreparationTime, ng.metrics.queryPrepareTime) - mint, maxt := ng.findMinMaxTime(s) - querier, err := query.queryable.Querier(ctxPrepare, mint, maxt) - if err != nil { -- prepareSpanTimer.Finish() - return nil, nil, err - } - defer querier.Close() - - ng.populateSeries(querier, s) -- prepareSpanTimer.Finish() - - // Modify the offset of vector and matrix selectors for the @ modifier - // w.r.t. the start time since only 1 evaluation will be done on them. - setOffsetForAtModifier(timeMilliseconds(s.Start), s.Expr) -! evalSpanTimer, ctxInnerEval := query.stats.GetSpanTimer(ctx, stats.InnerEvalTime, ng.metrics.queryInnerEval) - // Instant evaluation. This is executed as a range evaluation with one step. - if s.Start == s.End && s.Interval == 0 { - start := timeMilliseconds(s.Start) ---- 492,511 ---- - - // execEvalStmt evaluates the expression of an evaluation statement for the given time range. - func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *parser.EvalStmt) (parser.Value, storage.Warnings, error) { -! ctxPrepare := ctx - mint, maxt := ng.findMinMaxTime(s) - querier, err := query.queryable.Querier(ctxPrepare, mint, maxt) - if err != nil { - return nil, nil, err - } - defer querier.Close() - - ng.populateSeries(querier, s) - - // Modify the offset of vector and matrix selectors for the @ modifier - // w.r.t. the start time since only 1 evaluation will be done on them. - setOffsetForAtModifier(timeMilliseconds(s.Start), s.Expr) -! ctxInnerEval := ctx - // Instant evaluation. This is executed as a range evaluation with one step. - if s.Start == s.End && s.Interval == 0 { - start := timeMilliseconds(s.Start) -*************** -*** 637,654 **** - maxSamples: ng.maxSamplesPerQuery, - logger: ng.logger, - lookbackDelta: ng.lookbackDelta, -- samplesStats: query.sampleStats, - noStepSubqueryIntervalFn: ng.noStepSubqueryIntervalFn, - } -- query.sampleStats.InitStepTracking(start, start, 1) - - val, warnings, err := evaluator.Eval(s.Expr) - if err != nil { - return nil, warnings, err - } - -- evalSpanTimer.Finish() -- - var mat Matrix - - switch result := val.(type) { ---- 517,530 ---- -*************** -*** 689,703 **** - maxSamples: ng.maxSamplesPerQuery, - logger: ng.logger, - lookbackDelta: ng.lookbackDelta, -- samplesStats: query.sampleStats, - noStepSubqueryIntervalFn: ng.noStepSubqueryIntervalFn, - } -- query.sampleStats.InitStepTracking(evaluator.startTimestamp, evaluator.endTimestamp, evaluator.interval) - val, warnings, err := evaluator.Eval(s.Expr) - if err != nil { - return nil, warnings, err - } -- evalSpanTimer.Finish() - - mat, ok := val.(Matrix) - if !ok { ---- 565,576 ---- -*************** -*** 710,718 **** - } - - // TODO(fabxc): where to ensure metric labels are a copy from the storage internals. -- sortSpanTimer, _ := query.stats.GetSpanTimer(ctx, stats.ResultSortTime, ng.metrics.queryResultSort) - sort.Sort(mat) -- sortSpanTimer.Finish() - - return mat, warnings, nil - } ---- 583,589 ---- -*************** -*** 922,928 **** - currentSamples int - logger log.Logger - lookbackDelta time.Duration -- samplesStats *stats.QuerySamples - noStepSubqueryIntervalFn func(rangeMillis int64) int64 - } - ---- 793,798 ---- -*************** -*** 1115,1121 **** - } - } - args[i] = vectors[i] -- ev.samplesStats.UpdatePeak(ev.currentSamples) - } - - // Make the function call. ---- 985,990 ---- -*************** -*** 1131,1142 **** - // When we reset currentSamples to tempNumSamples during the next iteration of the loop it also - // needs to include the samples from the result here, as they're still in memory. - tempNumSamples += len(result) -- ev.samplesStats.UpdatePeak(ev.currentSamples) - - if ev.currentSamples > ev.maxSamples { - ev.error(ErrTooManySamples(env)) - } -- ev.samplesStats.UpdatePeak(ev.currentSamples) - - // If this could be an instant query, shortcut so as not to change sort order. - if ev.endTimestamp == ev.startTimestamp { ---- 1000,1009 ---- -*************** -*** 1146,1152 **** - mat[i] = Series{Metric: s.Metric, Points: []Point{s.Point}} - } - ev.currentSamples = originalNumSamples + mat.TotalSamples() -- ev.samplesStats.UpdatePeak(ev.currentSamples) - return mat, warnings - } - ---- 1013,1018 ---- -*************** -*** 1179,1198 **** - mat = append(mat, ss) - } - ev.currentSamples = originalNumSamples + mat.TotalSamples() -- ev.samplesStats.UpdatePeak(ev.currentSamples) - return mat, warnings - } - - // evalSubquery evaluates given SubqueryExpr and returns an equivalent - // evaluated MatrixSelector in its place. Note that the Name and LabelMatchers are not set. - func (ev *evaluator) evalSubquery(subq *parser.SubqueryExpr) (*parser.MatrixSelector, int, storage.Warnings) { -- samplesStats := ev.samplesStats -- // Avoid double counting samples when running a subquery, those samples will be counted in later stage. -- ev.samplesStats = ev.samplesStats.NewChild() - val, ws := ev.eval(subq) - // But do incorporate the peak from the subquery -- samplesStats.UpdatePeakFromSubquery(ev.samplesStats) -- ev.samplesStats = samplesStats - mat := val.(Matrix) - vs := &parser.VectorSelector{ - OriginalOffset: subq.OriginalOffset, ---- 1045,1058 ---- -*************** -*** 1227,1235 **** - numSteps := int((ev.endTimestamp-ev.startTimestamp)/ev.interval) + 1 - - // Create a new span to help investigate inner evaluation performances. -! ctxWithSpan, span := otel.Tracer("").Start(ev.ctx, stats.InnerEvalTime.SpanOperation()+" eval "+reflect.TypeOf(expr).String()) - ev.ctx = ctxWithSpan -- defer span.End() - - switch e := expr.(type) { - case *parser.AggregateExpr: ---- 1087,1094 ---- - numSteps := int((ev.endTimestamp-ev.startTimestamp)/ev.interval) + 1 - - // Create a new span to help investigate inner evaluation performances. -! ctxWithSpan := ev.ctx - ev.ctx = ctxWithSpan - - switch e := expr.(type) { - case *parser.AggregateExpr: -*************** -*** 1398,1404 **** - enh.Ts = ts - // Make the function call. - outVec := call(inArgs, e.Args, enh) -- ev.samplesStats.IncrementSamplesAtStep(step, int64(len(points))) - enh.Out = outVec[:0] - if len(outVec) > 0 { - ss.Points = append(ss.Points, Point{V: outVec[0].Point.V, T: ts}) ---- 1257,1262 ---- -*************** -*** 1416,1424 **** - } else { - putPointSlice(ss.Points) - } -- ev.samplesStats.UpdatePeak(ev.currentSamples) - } -- ev.samplesStats.UpdatePeak(ev.currentSamples) - - ev.currentSamples -= len(points) - putPointSlice(points) ---- 1274,1280 ---- -*************** -*** 1558,1564 **** - if ok { - if ev.currentSamples < ev.maxSamples { - ss.Points = append(ss.Points, Point{V: v, T: ts}) -- ev.samplesStats.IncrementSamplesAtStep(step, 1) - ev.currentSamples++ - } else { - ev.error(ErrTooManySamples(env)) ---- 1414,1419 ---- -*************** -*** 1572,1578 **** - putPointSlice(ss.Points) - } - } -- ev.samplesStats.UpdatePeak(ev.currentSamples) - return mat, ws - - case *parser.MatrixSelector: ---- 1427,1432 ---- -*************** -*** 1591,1597 **** - maxSamples: ev.maxSamples, - logger: ev.logger, - lookbackDelta: ev.lookbackDelta, -- samplesStats: ev.samplesStats.NewChild(), - noStepSubqueryIntervalFn: ev.noStepSubqueryIntervalFn, - } - ---- 1445,1450 ---- -*************** -*** 1617,1624 **** - - res, ws := newEv.eval(e.Expr) - ev.currentSamples = newEv.currentSamples -- ev.samplesStats.UpdatePeakFromSubquery(newEv.samplesStats) -- ev.samplesStats.IncrementSamplesAtTimestamp(ev.endTimestamp, newEv.samplesStats.TotalSamples) - return res, ws - case *parser.StepInvariantExpr: - switch ce := e.Expr.(type) { ---- 1470,1475 ---- -*************** -*** 1635,1649 **** - maxSamples: ev.maxSamples, - logger: ev.logger, - lookbackDelta: ev.lookbackDelta, -- samplesStats: ev.samplesStats.NewChild(), - noStepSubqueryIntervalFn: ev.noStepSubqueryIntervalFn, - } - res, ws := newEv.eval(e.Expr) - ev.currentSamples = newEv.currentSamples -- ev.samplesStats.UpdatePeakFromSubquery(newEv.samplesStats) - for ts, step := ev.startTimestamp, -1; ts <= ev.endTimestamp; ts = ts + ev.interval { - step++ -- ev.samplesStats.IncrementSamplesAtStep(step, newEv.samplesStats.TotalSamples) - } - switch e.Expr.(type) { - case *parser.MatrixSelector, *parser.SubqueryExpr: ---- 1486,1497 ---- -*************** -*** 1674,1680 **** - } - } - } -- ev.samplesStats.UpdatePeak(ev.currentSamples) - return res, ws - } - ---- 1522,1527 ---- -*************** -*** 1700,1713 **** - }) - - ev.currentSamples++ -- ev.samplesStats.IncrementSamplesAtTimestamp(ts, 1) - if ev.currentSamples > ev.maxSamples { - ev.error(ErrTooManySamples(env)) - } - } - - } -- ev.samplesStats.UpdatePeak(ev.currentSamples) - return vec, ws - } - ---- 1547,1558 ---- -*************** -*** 1740,1746 **** - return t, v, true - } - -! var pointPool = sync.Pool{} - - func getPointSlice(sz int) []Point { - p := pointPool.Get() ---- 1585,1595 ---- - return t, v, true - } - -! type fakePointPool struct {} -! func (f fakePointPool) Get() interface{} { return nil } -! func (f fakePointPool) Put(x any) { } -! -! var pointPool = fakePointPool{} - - func getPointSlice(sz int) []Point { - p := pointPool.Get() -*************** -*** 1783,1789 **** - } - - ss.Points = ev.matrixIterSlice(it, mint, maxt, getPointSlice(16)) -- ev.samplesStats.IncrementSamplesAtTimestamp(ev.startTimestamp, int64(len(ss.Points))) - - if len(ss.Points) > 0 { - matrix = append(matrix, ss) ---- 1632,1637 ---- -*************** -*** 1855,1861 **** - ev.currentSamples++ - } - } -- ev.samplesStats.UpdatePeak(ev.currentSamples) - return out - } - ---- 1703,1708 ---- -diff -c 'vendor/github.com/prometheus/prometheus/promql/functions.go' '_vendor/github.com/prometheus/prometheus/promql/functions.go' -Index: ./github.com/prometheus/prometheus/promql/functions.go -*** ./github.com/prometheus/prometheus/promql/functions.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/prometheus/promql/functions.go Mon Mar 11 17:57:07 2024 -*************** -*** 887,893 **** - if err != nil { - panic(fmt.Errorf("invalid regular expression in label_replace(): %s", regexStr)) - } -! if !model.LabelNameRE.MatchString(dst) { - panic(fmt.Errorf("invalid destination label name in label_replace(): %s", dst)) - } - enh.Dmn = make(map[uint64]labels.Labels, len(enh.Out)) ---- 887,893 ---- - if err != nil { - panic(fmt.Errorf("invalid regular expression in label_replace(): %s", regexStr)) - } -! if !model.LabelNameRE().MatchString(dst) { - panic(fmt.Errorf("invalid destination label name in label_replace(): %s", dst)) - } - enh.Dmn = make(map[uint64]labels.Labels, len(enh.Out)) -diff -c 'vendor/github.com/prometheus/prometheus/promql/parser/parse.go' '_vendor/github.com/prometheus/prometheus/promql/parser/parse.go' -Index: ./github.com/prometheus/prometheus/promql/parser/parse.go -*** ./github.com/prometheus/prometheus/promql/parser/parse.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/prometheus/promql/parser/parse.go Mon Mar 11 11:21:23 2024 -*************** -*** 21,27 **** - "runtime" - "strconv" - "strings" -- "sync" - "time" - - "github.com/prometheus/common/model" ---- 21,26 ---- -*************** -*** 31,41 **** - "github.com/prometheus/prometheus/util/strutil" - ) - -! var parserPool = sync.Pool{ -! New: func() interface{} { -! return &parser{} -! }, -! } - - type parser struct { - lex Lexer ---- 30,40 ---- - "github.com/prometheus/prometheus/util/strutil" - ) - -! type fakePool[T any] struct {} -! func (f fakePool[T]) Get() interface{} { return new(T) } -! func (f fakePool[T]) Put(x any) { } -! -! var parserPool = fakePool[parser]{} - - type parser struct { - lex Lexer -diff -c 'vendor/github.com/prometheus/prometheus/storage/generic.go' '_vendor/github.com/prometheus/prometheus/storage/generic.go' -Index: ./github.com/prometheus/prometheus/storage/generic.go -*** ./github.com/prometheus/prometheus/storage/generic.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/prometheus/storage/generic.go Tue Nov 7 15:29:20 2023 -*************** -*** 105,134 **** - return &chunkSeriesSetAdapter{q.genericQuerier.Select(sortSeries, hints, matchers...)} - } - -- type seriesMergerAdapter struct { -- VerticalSeriesMergeFunc -- } -- -- func (a *seriesMergerAdapter) Merge(s ...Labels) Labels { -- buf := make([]Series, 0, len(s)) -- for _, ser := range s { -- buf = append(buf, ser.(Series)) -- } -- return a.VerticalSeriesMergeFunc(buf...) -- } -- -- type chunkSeriesMergerAdapter struct { -- VerticalChunkSeriesMergeFunc -- } -- -- func (a *chunkSeriesMergerAdapter) Merge(s ...Labels) Labels { -- buf := make([]ChunkSeries, 0, len(s)) -- for _, ser := range s { -- buf = append(buf, ser.(ChunkSeries)) -- } -- return a.VerticalChunkSeriesMergeFunc(buf...) -- } -- - type noopGenericSeriesSet struct{} - - func (noopGenericSeriesSet) Next() bool { return false } ---- 105,110 ---- -diff -c 'vendor/github.com/prometheus/prometheus/tsdb/chunks/chunks.go' '_vendor/github.com/prometheus/prometheus/tsdb/chunks/chunks.go' -Index: ./github.com/prometheus/prometheus/tsdb/chunks/chunks.go -*** ./github.com/prometheus/prometheus/tsdb/chunks/chunks.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/prometheus/tsdb/chunks/chunks.go Tue Nov 7 15:39:45 2023 -*************** -*** 14,55 **** - package chunks - - import ( -- "bufio" -- "encoding/binary" -- "fmt" -- "hash" -- "hash/crc32" -- "io" -- "os" -- "path/filepath" -- "strconv" -- -- "github.com/pkg/errors" -- - "github.com/prometheus/prometheus/tsdb/chunkenc" -- tsdb_errors "github.com/prometheus/prometheus/tsdb/errors" -- "github.com/prometheus/prometheus/tsdb/fileutil" -- ) -- -- // Segment header fields constants. -- const ( -- // MagicChunks is 4 bytes at the head of a series file. -- MagicChunks = 0x85BD40DD -- // MagicChunksSize is the size in bytes of MagicChunks. -- MagicChunksSize = 4 -- chunksFormatV1 = 1 -- ChunksFormatVersionSize = 1 -- segmentHeaderPaddingSize = 3 -- // SegmentHeaderSize defines the total size of the header part. -- SegmentHeaderSize = MagicChunksSize + ChunksFormatVersionSize + segmentHeaderPaddingSize -- ) -- -- // Chunk fields constants. -- const ( -- // MaxChunkLengthFieldSize defines the maximum size of the data length part. -- MaxChunkLengthFieldSize = binary.MaxVarintLen32 -- // ChunkEncodingSize defines the size of the chunk encoding part. -- ChunkEncodingSize = 1 - ) - - // ChunkRef is a generic reference for reading chunk data. In prometheus it ---- 14,20 ---- -*************** -*** 57,114 **** - // may have their own reference types. - type ChunkRef uint64 - -- // HeadSeriesRef refers to in-memory series. -- type HeadSeriesRef uint64 -- -- // HeadChunkRef packs a HeadSeriesRef and a ChunkID into a global 8 Byte ID. -- // The HeadSeriesRef and ChunkID may not exceed 5 and 3 bytes respectively. -- type HeadChunkRef uint64 -- -- func NewHeadChunkRef(hsr HeadSeriesRef, chunkID HeadChunkID) HeadChunkRef { -- if hsr > (1<<40)-1 { -- panic("series ID exceeds 5 bytes") -- } -- if chunkID > (1<<24)-1 { -- panic("chunk ID exceeds 3 bytes") -- } -- return HeadChunkRef(uint64(hsr<<24) | uint64(chunkID)) -- } -- -- func (p HeadChunkRef) Unpack() (HeadSeriesRef, HeadChunkID) { -- return HeadSeriesRef(p >> 24), HeadChunkID(p<<40) >> 40 -- } -- -- // HeadChunkID refers to a specific chunk in a series (memSeries) in the Head. -- // Each memSeries has its own monotonically increasing number to refer to its chunks. -- // If the HeadChunkID value is... -- // * memSeries.firstChunkID+len(memSeries.mmappedChunks), it's the head chunk. -- // * less than the above, but >= memSeries.firstID, then it's -- // memSeries.mmappedChunks[i] where i = HeadChunkID - memSeries.firstID. -- // Example: -- // assume a memSeries.firstChunkID=7 and memSeries.mmappedChunks=[p5,p6,p7,p8,p9]. -- // | HeadChunkID value | refers to ... | -- // |-------------------|----------------------------------------------------------------------------------------| -- // | 0-6 | chunks that have been compacted to blocks, these won't return data for queries in Head | -- // | 7-11 | memSeries.mmappedChunks[i] where i is 0 to 4. | -- // | 12 | memSeries.headChunk | -- type HeadChunkID uint64 -- -- // BlockChunkRef refers to a chunk within a persisted block. -- // The upper 4 bytes are for the segment index and -- // the lower 4 bytes are for the segment offset where the data starts for this chunk. -- type BlockChunkRef uint64 -- -- // NewBlockChunkRef packs the file index and byte offset into a BlockChunkRef. -- func NewBlockChunkRef(fileIndex, fileOffset uint64) BlockChunkRef { -- return BlockChunkRef(fileIndex<<32 | fileOffset) -- } -- -- func (b BlockChunkRef) Unpack() (int, int) { -- sgmIndex := int(b >> 32) -- chkStart := int((b << 32) >> 32) -- return sgmIndex, chkStart -- } -- - // Meta holds information about a chunk of data. - type Meta struct { - // Ref and Chunk hold either a reference that can be used to retrieve ---- 22,27 ---- -*************** -*** 132,636 **** - // Err returns optional error if Next is false. - Err() error - } -- -- // writeHash writes the chunk encoding and raw data into the provided hash. -- func (cm *Meta) writeHash(h hash.Hash, buf []byte) error { -- buf = append(buf[:0], byte(cm.Chunk.Encoding())) -- if _, err := h.Write(buf[:1]); err != nil { -- return err -- } -- if _, err := h.Write(cm.Chunk.Bytes()); err != nil { -- return err -- } -- return nil -- } -- -- // OverlapsClosedInterval Returns true if the chunk overlaps [mint, maxt]. -- func (cm *Meta) OverlapsClosedInterval(mint, maxt int64) bool { -- // The chunk itself is a closed interval [cm.MinTime, cm.MaxTime]. -- return cm.MinTime <= maxt && mint <= cm.MaxTime -- } -- -- var errInvalidSize = fmt.Errorf("invalid size") -- -- var castagnoliTable *crc32.Table -- -- func init() { -- castagnoliTable = crc32.MakeTable(crc32.Castagnoli) -- } -- -- // newCRC32 initializes a CRC32 hash with a preconfigured polynomial, so the -- // polynomial may be easily changed in one location at a later time, if necessary. -- func newCRC32() hash.Hash32 { -- return crc32.New(castagnoliTable) -- } -- -- // Check if the CRC of data matches that stored in sum, computed when the chunk was stored. -- func checkCRC32(data, sum []byte) error { -- got := crc32.Checksum(data, castagnoliTable) -- // This combination of shifts is the inverse of digest.Sum() in go/src/hash/crc32. -- want := uint32(sum[0])<<24 + uint32(sum[1])<<16 + uint32(sum[2])<<8 + uint32(sum[3]) -- if got != want { -- return errors.Errorf("checksum mismatch expected:%x, actual:%x", want, got) -- } -- return nil -- } -- -- // Writer implements the ChunkWriter interface for the standard -- // serialization format. -- type Writer struct { -- dirFile *os.File -- files []*os.File -- wbuf *bufio.Writer -- n int64 -- crc32 hash.Hash -- buf [binary.MaxVarintLen32]byte -- -- segmentSize int64 -- } -- -- const ( -- // DefaultChunkSegmentSize is the default chunks segment size. -- DefaultChunkSegmentSize = 512 * 1024 * 1024 -- ) -- -- // NewWriterWithSegSize returns a new writer against the given directory -- // and allows setting a custom size for the segments. -- func NewWriterWithSegSize(dir string, segmentSize int64) (*Writer, error) { -- return newWriter(dir, segmentSize) -- } -- -- // NewWriter returns a new writer against the given directory -- // using the default segment size. -- func NewWriter(dir string) (*Writer, error) { -- return newWriter(dir, DefaultChunkSegmentSize) -- } -- -- func newWriter(dir string, segmentSize int64) (*Writer, error) { -- if segmentSize <= 0 { -- segmentSize = DefaultChunkSegmentSize -- } -- -- if err := os.MkdirAll(dir, 0o777); err != nil { -- return nil, err -- } -- dirFile, err := fileutil.OpenDir(dir) -- if err != nil { -- return nil, err -- } -- return &Writer{ -- dirFile: dirFile, -- n: 0, -- crc32: newCRC32(), -- segmentSize: segmentSize, -- }, nil -- } -- -- func (w *Writer) tail() *os.File { -- if len(w.files) == 0 { -- return nil -- } -- return w.files[len(w.files)-1] -- } -- -- // finalizeTail writes all pending data to the current tail file, -- // truncates its size, and closes it. -- func (w *Writer) finalizeTail() error { -- tf := w.tail() -- if tf == nil { -- return nil -- } -- -- if err := w.wbuf.Flush(); err != nil { -- return err -- } -- if err := tf.Sync(); err != nil { -- return err -- } -- // As the file was pre-allocated, we truncate any superfluous zero bytes. -- off, err := tf.Seek(0, io.SeekCurrent) -- if err != nil { -- return err -- } -- if err := tf.Truncate(off); err != nil { -- return err -- } -- -- return tf.Close() -- } -- -- func (w *Writer) cut() error { -- // Sync current tail to disk and close. -- if err := w.finalizeTail(); err != nil { -- return err -- } -- -- n, f, _, err := cutSegmentFile(w.dirFile, MagicChunks, chunksFormatV1, w.segmentSize) -- if err != nil { -- return err -- } -- w.n = int64(n) -- -- w.files = append(w.files, f) -- if w.wbuf != nil { -- w.wbuf.Reset(f) -- } else { -- w.wbuf = bufio.NewWriterSize(f, 8*1024*1024) -- } -- -- return nil -- } -- -- func cutSegmentFile(dirFile *os.File, magicNumber uint32, chunksFormat byte, allocSize int64) (headerSize int, newFile *os.File, seq int, returnErr error) { -- p, seq, err := nextSequenceFile(dirFile.Name()) -- if err != nil { -- return 0, nil, 0, errors.Wrap(err, "next sequence file") -- } -- ptmp := p + ".tmp" -- f, err := os.OpenFile(ptmp, os.O_WRONLY|os.O_CREATE, 0o666) -- if err != nil { -- return 0, nil, 0, errors.Wrap(err, "open temp file") -- } -- defer func() { -- if returnErr != nil { -- errs := tsdb_errors.NewMulti(returnErr) -- if f != nil { -- errs.Add(f.Close()) -- } -- // Calling RemoveAll on a non-existent file does not return error. -- errs.Add(os.RemoveAll(ptmp)) -- returnErr = errs.Err() -- } -- }() -- if allocSize > 0 { -- if err = fileutil.Preallocate(f, allocSize, true); err != nil { -- return 0, nil, 0, errors.Wrap(err, "preallocate") -- } -- } -- if err = dirFile.Sync(); err != nil { -- return 0, nil, 0, errors.Wrap(err, "sync directory") -- } -- -- // Write header metadata for new file. -- metab := make([]byte, SegmentHeaderSize) -- binary.BigEndian.PutUint32(metab[:MagicChunksSize], magicNumber) -- metab[4] = chunksFormat -- -- n, err := f.Write(metab) -- if err != nil { -- return 0, nil, 0, errors.Wrap(err, "write header") -- } -- if err := f.Close(); err != nil { -- return 0, nil, 0, errors.Wrap(err, "close temp file") -- } -- f = nil -- -- if err := fileutil.Rename(ptmp, p); err != nil { -- return 0, nil, 0, errors.Wrap(err, "replace file") -- } -- -- f, err = os.OpenFile(p, os.O_WRONLY, 0o666) -- if err != nil { -- return 0, nil, 0, errors.Wrap(err, "open final file") -- } -- // Skip header for further writes. -- if _, err := f.Seek(int64(n), 0); err != nil { -- return 0, nil, 0, errors.Wrap(err, "seek in final file") -- } -- return n, f, seq, nil -- } -- -- func (w *Writer) write(b []byte) error { -- n, err := w.wbuf.Write(b) -- w.n += int64(n) -- return err -- } -- -- // WriteChunks writes as many chunks as possible to the current segment, -- // cuts a new segment when the current segment is full and -- // writes the rest of the chunks in the new segment. -- func (w *Writer) WriteChunks(chks ...Meta) error { -- var ( -- batchSize = int64(0) -- batchStart = 0 -- batches = make([][]Meta, 1) -- batchID = 0 -- firstBatch = true -- ) -- -- for i, chk := range chks { -- // Each chunk contains: data length + encoding + the data itself + crc32 -- chkSize := int64(MaxChunkLengthFieldSize) // The data length is a variable length field so use the maximum possible value. -- chkSize += ChunkEncodingSize // The chunk encoding. -- chkSize += int64(len(chk.Chunk.Bytes())) // The data itself. -- chkSize += crc32.Size // The 4 bytes of crc32. -- batchSize += chkSize -- -- // Cut a new batch when it is not the first chunk(to avoid empty segments) and -- // the batch is too large to fit in the current segment. -- cutNewBatch := (i != 0) && (batchSize+SegmentHeaderSize > w.segmentSize) -- -- // When the segment already has some data than -- // the first batch size calculation should account for that. -- if firstBatch && w.n > SegmentHeaderSize { -- cutNewBatch = batchSize+w.n > w.segmentSize -- if cutNewBatch { -- firstBatch = false -- } -- } -- -- if cutNewBatch { -- batchStart = i -- batches = append(batches, []Meta{}) -- batchID++ -- batchSize = chkSize -- } -- batches[batchID] = chks[batchStart : i+1] -- } -- -- // Create a new segment when one doesn't already exist. -- if w.n == 0 { -- if err := w.cut(); err != nil { -- return err -- } -- } -- -- for i, chks := range batches { -- if err := w.writeChunks(chks); err != nil { -- return err -- } -- // Cut a new segment only when there are more chunks to write. -- // Avoid creating a new empty segment at the end of the write. -- if i < len(batches)-1 { -- if err := w.cut(); err != nil { -- return err -- } -- } -- } -- return nil -- } -- -- // writeChunks writes the chunks into the current segment irrespective -- // of the configured segment size limit. A segment should have been already -- // started before calling this. -- func (w *Writer) writeChunks(chks []Meta) error { -- if len(chks) == 0 { -- return nil -- } -- -- seq := uint64(w.seq()) -- for i := range chks { -- chk := &chks[i] -- -- chk.Ref = ChunkRef(NewBlockChunkRef(seq, uint64(w.n))) -- -- n := binary.PutUvarint(w.buf[:], uint64(len(chk.Chunk.Bytes()))) -- -- if err := w.write(w.buf[:n]); err != nil { -- return err -- } -- w.buf[0] = byte(chk.Chunk.Encoding()) -- if err := w.write(w.buf[:1]); err != nil { -- return err -- } -- if err := w.write(chk.Chunk.Bytes()); err != nil { -- return err -- } -- -- w.crc32.Reset() -- if err := chk.writeHash(w.crc32, w.buf[:]); err != nil { -- return err -- } -- if err := w.write(w.crc32.Sum(w.buf[:0])); err != nil { -- return err -- } -- } -- return nil -- } -- -- func (w *Writer) seq() int { -- return len(w.files) - 1 -- } -- -- func (w *Writer) Close() error { -- if err := w.finalizeTail(); err != nil { -- return err -- } -- -- // close dir file (if not windows platform will fail on rename) -- return w.dirFile.Close() -- } -- -- // ByteSlice abstracts a byte slice. -- type ByteSlice interface { -- Len() int -- Range(start, end int) []byte -- } -- -- type realByteSlice []byte -- -- func (b realByteSlice) Len() int { -- return len(b) -- } -- -- func (b realByteSlice) Range(start, end int) []byte { -- return b[start:end] -- } -- -- // Reader implements a ChunkReader for a serialized byte stream -- // of series data. -- type Reader struct { -- // The underlying bytes holding the encoded series data. -- // Each slice holds the data for a different segment. -- bs []ByteSlice -- cs []io.Closer // Closers for resources behind the byte slices. -- size int64 // The total size of bytes in the reader. -- pool chunkenc.Pool -- } -- -- func newReader(bs []ByteSlice, cs []io.Closer, pool chunkenc.Pool) (*Reader, error) { -- cr := Reader{pool: pool, bs: bs, cs: cs} -- for i, b := range cr.bs { -- if b.Len() < SegmentHeaderSize { -- return nil, errors.Wrapf(errInvalidSize, "invalid segment header in segment %d", i) -- } -- // Verify magic number. -- if m := binary.BigEndian.Uint32(b.Range(0, MagicChunksSize)); m != MagicChunks { -- return nil, errors.Errorf("invalid magic number %x", m) -- } -- -- // Verify chunk format version. -- if v := int(b.Range(MagicChunksSize, MagicChunksSize+ChunksFormatVersionSize)[0]); v != chunksFormatV1 { -- return nil, errors.Errorf("invalid chunk format version %d", v) -- } -- cr.size += int64(b.Len()) -- } -- return &cr, nil -- } -- -- // NewDirReader returns a new Reader against sequentially numbered files in the -- // given directory. -- func NewDirReader(dir string, pool chunkenc.Pool) (*Reader, error) { -- files, err := sequenceFiles(dir) -- if err != nil { -- return nil, err -- } -- if pool == nil { -- pool = chunkenc.NewPool() -- } -- -- var ( -- bs []ByteSlice -- cs []io.Closer -- ) -- for _, fn := range files { -- f, err := fileutil.OpenMmapFile(fn) -- if err != nil { -- return nil, tsdb_errors.NewMulti( -- errors.Wrap(err, "mmap files"), -- tsdb_errors.CloseAll(cs), -- ).Err() -- } -- cs = append(cs, f) -- bs = append(bs, realByteSlice(f.Bytes())) -- } -- -- reader, err := newReader(bs, cs, pool) -- if err != nil { -- return nil, tsdb_errors.NewMulti( -- err, -- tsdb_errors.CloseAll(cs), -- ).Err() -- } -- return reader, nil -- } -- -- func (s *Reader) Close() error { -- return tsdb_errors.CloseAll(s.cs) -- } -- -- // Size returns the size of the chunks. -- func (s *Reader) Size() int64 { -- return s.size -- } -- -- // Chunk returns a chunk from a given reference. -- func (s *Reader) Chunk(ref ChunkRef) (chunkenc.Chunk, error) { -- sgmIndex, chkStart := BlockChunkRef(ref).Unpack() -- -- if sgmIndex >= len(s.bs) { -- return nil, errors.Errorf("segment index %d out of range", sgmIndex) -- } -- -- sgmBytes := s.bs[sgmIndex] -- -- if chkStart+MaxChunkLengthFieldSize > sgmBytes.Len() { -- return nil, errors.Errorf("segment doesn't include enough bytes to read the chunk size data field - required:%v, available:%v", chkStart+MaxChunkLengthFieldSize, sgmBytes.Len()) -- } -- // With the minimum chunk length this should never cause us reading -- // over the end of the slice. -- c := sgmBytes.Range(chkStart, chkStart+MaxChunkLengthFieldSize) -- chkDataLen, n := binary.Uvarint(c) -- if n <= 0 { -- return nil, errors.Errorf("reading chunk length failed with %d", n) -- } -- -- chkEncStart := chkStart + n -- chkEnd := chkEncStart + ChunkEncodingSize + int(chkDataLen) + crc32.Size -- chkDataStart := chkEncStart + ChunkEncodingSize -- chkDataEnd := chkEnd - crc32.Size -- -- if chkEnd > sgmBytes.Len() { -- return nil, errors.Errorf("segment doesn't include enough bytes to read the chunk - required:%v, available:%v", chkEnd, sgmBytes.Len()) -- } -- -- sum := sgmBytes.Range(chkDataEnd, chkEnd) -- if err := checkCRC32(sgmBytes.Range(chkEncStart, chkDataEnd), sum); err != nil { -- return nil, err -- } -- -- chkData := sgmBytes.Range(chkDataStart, chkDataEnd) -- chkEnc := sgmBytes.Range(chkEncStart, chkEncStart+ChunkEncodingSize)[0] -- return s.pool.Get(chunkenc.Encoding(chkEnc), chkData) -- } -- -- func nextSequenceFile(dir string) (string, int, error) { -- files, err := os.ReadDir(dir) -- if err != nil { -- return "", 0, err -- } -- -- i := uint64(0) -- for _, f := range files { -- j, err := strconv.ParseUint(f.Name(), 10, 64) -- if err != nil { -- continue -- } -- // It is not necessary that we find the files in number order, -- // for example with '1000000' and '200000', '1000000' would come first. -- // Though this is a very very race case, we check anyway for the max id. -- if j > i { -- i = j -- } -- } -- return segmentFile(dir, int(i+1)), int(i + 1), nil -- } -- -- func segmentFile(baseDir string, index int) string { -- return filepath.Join(baseDir, fmt.Sprintf("%0.6d", index)) -- } -- -- func sequenceFiles(dir string) ([]string, error) { -- files, err := os.ReadDir(dir) -- if err != nil { -- return nil, err -- } -- var res []string -- for _, fi := range files { -- if _, err := strconv.ParseUint(fi.Name(), 10, 64); err != nil { -- continue -- } -- res = append(res, filepath.Join(dir, fi.Name())) -- } -- return res, nil -- } ---- 45,47 ---- -diff -c 'vendor/github.com/prometheus/prometheus/util/stats/query_stats.go' '_vendor/github.com/prometheus/prometheus/util/stats/query_stats.go' -Index: ./github.com/prometheus/prometheus/util/stats/query_stats.go -*** ./github.com/prometheus/prometheus/util/stats/query_stats.go Mon Mar 11 19:34:50 2024 ---- ./github.com/prometheus/prometheus/util/stats/query_stats.go Thu Oct 26 15:21:07 2023 -*************** -*** 19,26 **** - "fmt" - - "github.com/prometheus/client_golang/prometheus" -- "go.opentelemetry.io/otel" -- "go.opentelemetry.io/otel/trace" - ) - - // QueryTiming identifies the code area or functionality in which time is spent ---- 19,24 ---- -*************** -*** 189,219 **** - - // SpanTimer unifies tracing and timing, to reduce repetition. - type SpanTimer struct { -- timer *Timer -- observers []prometheus.Observer - -- span trace.Span - } - - func NewSpanTimer(ctx context.Context, operation string, timer *Timer, observers ...prometheus.Observer) (*SpanTimer, context.Context) { -- ctx, span := otel.Tracer("").Start(ctx, operation) -- timer.Start() - - return &SpanTimer{ -- timer: timer, -- observers: observers, -- -- span: span, - }, ctx - } - - func (s *SpanTimer) Finish() { -- s.timer.Stop() -- s.span.End() -- -- for _, obs := range s.observers { -- obs.Observe(s.timer.ElapsedTime().Seconds()) -- } - } - - type Statistics struct { ---- 187,202 ---- -#### End of Patch data #### - -#### ApplyPatch data follows #### -# Data version : 1.0 -# Date generated : Mon Mar 11 19:35:07 2024 -# Generated by : makepatch 2.03 -# Recurse directories : Yes -# Excluded files : (\A|/).*\~\Z -# (\A|/).*\.a\Z -# (\A|/).*\.bak\Z -# (\A|/).*\.BAK\Z -# (\A|/).*\.elc\Z -# (\A|/).*\.exe\Z -# (\A|/).*\.gz\Z -# (\A|/).*\.ln\Z -# (\A|/).*\.o\Z -# (\A|/).*\.obj\Z -# (\A|/).*\.olb\Z -# (\A|/).*\.old\Z -# (\A|/).*\.orig\Z -# (\A|/).*\.rej\Z -# (\A|/).*\.so\Z -# (\A|/).*\.Z\Z -# (\A|/)\.del\-.*\Z -# (\A|/)\.make\.state\Z -# (\A|/)\.nse_depinfo\Z -# (\A|/)core\Z -# (\A|/)tags\Z -# (\A|/)TAGS\Z -# r 'github.com/prometheus/prometheus/util/teststorage/storage.go' 2504 0 -# r 'github.com/prometheus/prometheus/tsdb/wal/watcher.go' 19515 0 -# r 'github.com/prometheus/prometheus/tsdb/wal/wal.go' 25923 0 -# r 'github.com/prometheus/prometheus/tsdb/wal/reader.go' 5511 0 -# r 'github.com/prometheus/prometheus/tsdb/wal/live_reader.go' 10173 0 -# r 'github.com/prometheus/prometheus/tsdb/wal/checkpoint.go' 9233 0 -# r 'github.com/prometheus/prometheus/tsdb/wal.go' 32167 0 -# r 'github.com/prometheus/prometheus/tsdb/tsdbutil/dir_locker_testutil.go' 2641 0 -# r 'github.com/prometheus/prometheus/tsdb/tsdbutil/dir_locker.go' 2672 0 -# r 'github.com/prometheus/prometheus/tsdb/tsdbutil/chunks.go' 2057 0 -# r 'github.com/prometheus/prometheus/tsdb/tsdbutil/buffer.go' 4706 0 -# r 'github.com/prometheus/prometheus/tsdb/tsdbblockutil.go' 2118 0 -# r 'github.com/prometheus/prometheus/tsdb/tombstones/tombstones.go' 9309 0 -# r 'github.com/prometheus/prometheus/tsdb/repair.go' 4149 0 -# r 'github.com/prometheus/prometheus/tsdb/record/record.go' 8531 0 -# r 'github.com/prometheus/prometheus/tsdb/querier.go' 24151 0 -# r 'github.com/prometheus/prometheus/tsdb/isolation.go' 7510 0 -# r 'github.com/prometheus/prometheus/tsdb/index/postingsstats.go' 1515 0 -# r 'github.com/prometheus/prometheus/tsdb/index/postings.go' 23016 0 -# r 'github.com/prometheus/prometheus/tsdb/index/index.go' 47541 0 -# r 'github.com/prometheus/prometheus/tsdb/head_wal.go' 29645 0 -# r 'github.com/prometheus/prometheus/tsdb/head_read.go' 14357 0 -# r 'github.com/prometheus/prometheus/tsdb/head_append.go' 18505 0 -# r 'github.com/prometheus/prometheus/tsdb/head.go' 54668 0 -# r 'github.com/prometheus/prometheus/tsdb/goversion/init.go' 721 0 -# r 'github.com/prometheus/prometheus/tsdb/goversion/goversion.go' 771 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/sync_linux.go' 932 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/sync_darwin.go' 830 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/sync.go' 826 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/preallocate_other.go' 857 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/preallocate_linux.go' 1403 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/preallocate_darwin.go' 1138 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/preallocate.go' 1535 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/mmap_windows.go' 1460 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/mmap_unix.go' 914 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/mmap_js.go' 833 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/mmap_arm64.go' 692 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/mmap_amd64.go' 692 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/mmap_386.go' 686 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/mmap.go' 1530 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/flock_windows.go' 1111 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/flock_unix.go' 1346 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/flock_solaris.go' 1338 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/flock_plan9.go' 921 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/flock_js.go' 926 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/flock.go' 1358 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/fileutil.go' 3108 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/dir_windows.go' 1465 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/dir_unix.go' 776 0 -# r 'github.com/prometheus/prometheus/tsdb/fileutil/dir.go' 915 0 -# r 'github.com/prometheus/prometheus/tsdb/exemplar.go' 13312 0 -# r 'github.com/prometheus/prometheus/tsdb/errors/errors.go' 2359 0 -# r 'github.com/prometheus/prometheus/tsdb/encoding/encoding.go' 7768 0 -# r 'github.com/prometheus/prometheus/tsdb/db.go' 54267 0 -# r 'github.com/prometheus/prometheus/tsdb/compact.go' 23042 0 -# r 'github.com/prometheus/prometheus/tsdb/chunks/queue.go' 4103 0 -# r 'github.com/prometheus/prometheus/tsdb/chunks/head_chunks_windows.go' 841 0 -# r 'github.com/prometheus/prometheus/tsdb/chunks/head_chunks_other.go' 1027 0 -# r 'github.com/prometheus/prometheus/tsdb/chunks/head_chunks.go' 33402 0 -# r 'github.com/prometheus/prometheus/tsdb/chunks/chunk_write_queue.go' 7635 0 -# r 'github.com/prometheus/prometheus/tsdb/blockwriter.go' 3900 0 -# r 'github.com/prometheus/prometheus/tsdb/block.go' 19310 0 -# r 'github.com/prometheus/prometheus/tsdb/README.md' 1520 0 -# r 'github.com/prometheus/prometheus/tsdb/CHANGELOG.md' 6690 0 -# r 'github.com/prometheus/prometheus/tsdb/.gitignore' 10 0 -# r 'github.com/prometheus/prometheus/storage/series.go' 8594 0 -# r 'github.com/prometheus/prometheus/storage/secondary.go' 3907 0 -# r 'github.com/prometheus/prometheus/storage/merge.go' 21223 0 -# r 'github.com/prometheus/prometheus/storage/lazy.go' 1868 0 -# r 'github.com/prometheus/prometheus/storage/fanout.go' 5846 0 -# r 'github.com/prometheus/prometheus/promql/test.go' 20825 0 -# r 'github.com/prometheus/prometheus/promql/query_logger.go' 5342 0 -# p 'github.com/alecthomas/participle/v2/validate.go' 1075 1698324773 0100664 -# p 'github.com/aws/aws-sdk-go/aws/defaults/defaults.go' 6835 1698322867 0100664 -# p 'github.com/aws/aws-sdk-go/aws/request/retryer.go' 8830 1698322867 0100664 -# p 'github.com/davecgh/go-spew/spew/bypass.go' 4715 1698322867 0100664 -# p 'github.com/edsrzf/mmap-go/mmap.go' 3653 1698322867 0100664 -# p 'github.com/grafana/regexp/backtrack.go' 8998 1710148638 0100664 -# p 'github.com/grafana/regexp/exec.go' 12568 1710148627 0100664 -# p 'github.com/grafana/regexp/regexp.go' 38456 1710173669 0100664 -# p 'github.com/mwitkow/go-conntrack/dialer_reporter.go' 3458 1698322867 0100664 -# p 'github.com/mwitkow/go-conntrack/listener_wrapper.go' 3833 1698322867 0100664 -# p 'github.com/pquerna/ffjson/fflib/v1/buffer_pool.go' 2368 1710174996 0100664 -# c 'github.com/pquerna/ffjson/inception/decoder.go' 0 1698319061 0100664 -# c 'github.com/pquerna/ffjson/inception/decoder_tpl.go' 0 1698319061 0100664 -# c 'github.com/pquerna/ffjson/inception/encoder.go' 0 1698319061 0100664 -# c 'github.com/pquerna/ffjson/inception/encoder_tpl.go' 0 1698319061 0100664 -# c 'github.com/pquerna/ffjson/inception/inception.go' 0 1698319061 0100664 -# c 'github.com/pquerna/ffjson/inception/reflect.go' 0 1698319061 0100664 -# c 'github.com/pquerna/ffjson/inception/tags.go' 0 1698319061 0100664 -# c 'github.com/pquerna/ffjson/inception/template.go' 0 1698319061 0100664 -# c 'github.com/pquerna/ffjson/inception/writerstack.go' 0 1698319061 0100664 -# c 'github.com/pquerna/ffjson/shared/options.go' 0 1698319061 0100664 -# p 'github.com/prometheus/client_golang/prometheus/go_collector.go' 8405 1698322867 0100664 -# p 'github.com/prometheus/client_golang/prometheus/go_collector_latest.go' 17751 1698322867 0100664 -# p 'github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go' 4867 1698322867 0100664 -# p 'github.com/prometheus/client_golang/prometheus/registry.go' 31929 1698322867 0100664 -# p 'github.com/prometheus/client_golang/prometheus/testutil/promlint/promlint.go' 10339 1710172049 0100664 -# p 'github.com/prometheus/common/model/labels.go' 6074 1710172585 0100664 -# p 'github.com/prometheus/common/model/metric.go' 2873 1710172673 0100664 -# p 'github.com/prometheus/common/model/silence.go' 2836 1710172049 0100664 -# p 'github.com/prometheus/common/model/time.go' 8095 1710172365 0100664 -# p 'github.com/prometheus/procfs/cpuinfo.go' 12479 1710172049 0100664 -# p 'github.com/prometheus/procfs/mdstat.go' 8590 1710172049 0100664 -# p 'github.com/prometheus/procfs/proc_fdinfo.go' 3523 1710172049 0100664 -# p 'github.com/prometheus/procfs/proc_limits.go' 4890 1710172040 0100664 -# p 'github.com/prometheus/procfs/proc_smaps.go' 3931 1710172049 0100664 -# p 'github.com/prometheus/procfs/proc_stat.go' 6165 1698322867 0100664 -# p 'github.com/prometheus/procfs/schedstat.go' 3083 1710172049 0100664 -# p 'github.com/prometheus/procfs/slab.go' 3584 1710172049 0100664 -# p 'github.com/prometheus/procfs/zoneinfo.go' 6375 1710172049 0100664 -# p 'github.com/prometheus/prometheus/discovery/registry.go' 7921 1698322867 0100664 -# p 'github.com/prometheus/prometheus/promql/engine.go' 83665 1710149215 0100664 -# p 'github.com/prometheus/prometheus/promql/functions.go' 38686 1710172627 0100664 -# p 'github.com/prometheus/prometheus/promql/parser/parse.go' 22682 1710148883 0100664 -# p 'github.com/prometheus/prometheus/storage/generic.go' 3707 1699363760 0100664 -# p 'github.com/prometheus/prometheus/tsdb/chunks/chunks.go' 18283 1699364385 0100664 -# p 'github.com/prometheus/prometheus/util/stats/query_stats.go' 8691 1698322867 0100664 -# R 'github.com/prometheus/prometheus/util/teststorage' -# R 'github.com/prometheus/prometheus/tsdb/wal' -# R 'github.com/prometheus/prometheus/tsdb/tsdbutil' -# R 'github.com/prometheus/prometheus/tsdb/tombstones' -# R 'github.com/prometheus/prometheus/tsdb/record' -# R 'github.com/prometheus/prometheus/tsdb/index' -# R 'github.com/prometheus/prometheus/tsdb/goversion' -# R 'github.com/prometheus/prometheus/tsdb/fileutil' -# R 'github.com/prometheus/prometheus/tsdb/errors' -# R 'github.com/prometheus/prometheus/tsdb/encoding' -# C 'github.com/pquerna/ffjson/inception' 0 1698319061 040775 -# C 'github.com/pquerna/ffjson/shared' 0 1698319061 040775 -#### End of ApplyPatch data #### - -#### End of Patch kit [created: Mon Mar 11 19:35:07 2024] #### -#### Patch checksum: 5480 166486 19883 #### -#### Checksum: 5614 174052 4911 #### diff --git a/wasm_parts/wasm_exec.js b/wasm_parts/wasm_exec.js deleted file mode 100644 index 5dfc67c3..00000000 --- a/wasm_parts/wasm_exec.js +++ /dev/null @@ -1,529 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// -// This file has been modified for use by the TinyGo compiler. - -(() => { - // Map multiple JavaScript environments to a single common API, - // preferring web standards over Node.js API. - // - // Environments considered: - // - Browsers - // - Node.js - // - Electron - // - Parcel - - if (typeof global !== "undefined") { - // global already exists - } else if (typeof window !== "undefined") { - window.global = window; - } else if (typeof self !== "undefined") { - self.global = self; - } else { - throw new Error("cannot export Go (neither global, window nor self is defined)"); - } - - if (!global.require && typeof require !== "undefined") { - global.require = require; - } - - if (!global.fs && global.require) { - global.fs = require("fs"); - } - - const enosys = () => { - const err = new Error("not implemented"); - err.code = "ENOSYS"; - return err; - }; - - if (!global.fs) { - let outputBuf = ""; - global.fs = { - constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused - writeSync(fd, buf) { - outputBuf += decoder.decode(buf); - const nl = outputBuf.lastIndexOf("\n"); - if (nl != -1) { - console.log(outputBuf.substr(0, nl)); - outputBuf = outputBuf.substr(nl + 1); - } - return buf.length; - }, - write(fd, buf, offset, length, position, callback) { - if (offset !== 0 || length !== buf.length || position !== null) { - callback(enosys()); - return; - } - const n = this.writeSync(fd, buf); - callback(null, n); - }, - chmod(path, mode, callback) { callback(enosys()); }, - chown(path, uid, gid, callback) { callback(enosys()); }, - close(fd, callback) { callback(enosys()); }, - fchmod(fd, mode, callback) { callback(enosys()); }, - fchown(fd, uid, gid, callback) { callback(enosys()); }, - fstat(fd, callback) { callback(enosys()); }, - fsync(fd, callback) { callback(null); }, - ftruncate(fd, length, callback) { callback(enosys()); }, - lchown(path, uid, gid, callback) { callback(enosys()); }, - link(path, link, callback) { callback(enosys()); }, - lstat(path, callback) { callback(enosys()); }, - mkdir(path, perm, callback) { callback(enosys()); }, - open(path, flags, mode, callback) { callback(enosys()); }, - read(fd, buffer, offset, length, position, callback) { callback(enosys()); }, - readdir(path, callback) { callback(enosys()); }, - readlink(path, callback) { callback(enosys()); }, - rename(from, to, callback) { callback(enosys()); }, - rmdir(path, callback) { callback(enosys()); }, - stat(path, callback) { callback(enosys()); }, - symlink(path, link, callback) { callback(enosys()); }, - truncate(path, length, callback) { callback(enosys()); }, - unlink(path, callback) { callback(enosys()); }, - utimes(path, atime, mtime, callback) { callback(enosys()); }, - }; - } - - if (!global.process) { - global.process = { - getuid() { return -1; }, - getgid() { return -1; }, - geteuid() { return -1; }, - getegid() { return -1; }, - getgroups() { throw enosys(); }, - pid: -1, - ppid: -1, - umask() { throw enosys(); }, - cwd() { throw enosys(); }, - chdir() { throw enosys(); }, - } - } - - if (!global.crypto) { - const nodeCrypto = require("crypto"); - global.crypto = { - getRandomValues(b) { - nodeCrypto.randomFillSync(b); - }, - }; - } - - if (!global.performance) { - global.performance = { - now() { - const [sec, nsec] = process.hrtime(); - return sec * 1000 + nsec / 1000000; - }, - }; - } - - if (!global.TextEncoder) { - global.TextEncoder = require("util").TextEncoder; - } - - if (!global.TextDecoder) { - global.TextDecoder = require("util").TextDecoder; - } - - // End of polyfills for common API. - - const encoder = new TextEncoder("utf-8"); - const decoder = new TextDecoder("utf-8"); - let reinterpretBuf = new DataView(new ArrayBuffer(8)); - var logLine = []; - - global.Go = class { - constructor() { - this._callbackTimeouts = new Map(); - this._nextCallbackTimeoutID = 1; - - const mem = () => { - // The buffer may change when requesting more memory. - return new DataView(this._inst.exports.memory.buffer); - } - - const unboxValue = (v_ref) => { - reinterpretBuf.setBigInt64(0, v_ref, true); - const f = reinterpretBuf.getFloat64(0, true); - if (f === 0) { - return undefined; - } - if (!isNaN(f)) { - return f; - } - - const id = v_ref & 0xffffffffn; - return this._values[id]; - } - - - const loadValue = (addr) => { - let v_ref = mem().getBigUint64(addr, true); - return unboxValue(v_ref); - } - - const boxValue = (v) => { - const nanHead = 0x7FF80000n; - - if (typeof v === "number") { - if (isNaN(v)) { - return nanHead << 32n; - } - if (v === 0) { - return (nanHead << 32n) | 1n; - } - reinterpretBuf.setFloat64(0, v, true); - return reinterpretBuf.getBigInt64(0, true); - } - - switch (v) { - case undefined: - return 0n; - case null: - return (nanHead << 32n) | 2n; - case true: - return (nanHead << 32n) | 3n; - case false: - return (nanHead << 32n) | 4n; - } - - let id = this._ids.get(v); - if (id === undefined) { - id = this._idPool.pop(); - if (id === undefined) { - id = BigInt(this._values.length); - } - this._values[id] = v; - this._goRefCounts[id] = 0; - this._ids.set(v, id); - } - this._goRefCounts[id]++; - let typeFlag = 1n; - switch (typeof v) { - case "string": - typeFlag = 2n; - break; - case "symbol": - typeFlag = 3n; - break; - case "function": - typeFlag = 4n; - break; - } - return id | ((nanHead | typeFlag) << 32n); - } - - const storeValue = (addr, v) => { - let v_ref = boxValue(v); - mem().setBigUint64(addr, v_ref, true); - } - - const loadSlice = (array, len, cap) => { - return new Uint8Array(this._inst.exports.memory.buffer, array, len); - } - - const loadSliceOfValues = (array, len, cap) => { - const a = new Array(len); - for (let i = 0; i < len; i++) { - a[i] = loadValue(array + i * 8); - } - return a; - } - - const loadString = (ptr, len) => { - return decoder.decode(new DataView(this._inst.exports.memory.buffer, ptr, len)); - } - - const timeOrigin = Date.now() - performance.now(); - this.importObject = { - wasi_snapshot_preview1: { - // https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#fd_write - fd_write: function(fd, iovs_ptr, iovs_len, nwritten_ptr) { - let nwritten = 0; - if (fd == 1) { - for (let iovs_i=0; iovs_i<iovs_len;iovs_i++) { - let iov_ptr = iovs_ptr+iovs_i*8; // assuming wasm32 - let ptr = mem().getUint32(iov_ptr + 0, true); - let len = mem().getUint32(iov_ptr + 4, true); - nwritten += len; - for (let i=0; i<len; i++) { - let c = mem().getUint8(ptr+i); - if (c == 13) { // CR - // ignore - } else if (c == 10) { // LF - // write line - let line = decoder.decode(new Uint8Array(logLine)); - logLine = []; - console.log(line); - } else { - logLine.push(c); - } - } - } - } else { - console.error('invalid file descriptor:', fd); - } - mem().setUint32(nwritten_ptr, nwritten, true); - return 0; - }, - fd_close: () => 0, // dummy - fd_fdstat_get: () => 0, // dummy - fd_seek: () => 0, // dummy - "proc_exit": (code) => { - if (global.process) { - // Node.js - process.exit(code); - } else { - // Can't exit in a browser. - throw 'trying to exit with code ' + code; - } - }, - random_get: (bufPtr, bufLen) => { - crypto.getRandomValues(loadSlice(bufPtr, bufLen)); - return 0; - }, - }, - gojs: { - // func ticks() float64 - "runtime.ticks": () => { - return timeOrigin + performance.now(); - }, - - // func sleepTicks(timeout float64) - "runtime.sleepTicks": (timeout) => { - // Do not sleep, only reactivate scheduler after the given timeout. - setTimeout(this._inst.exports.go_scheduler, timeout); - }, - - // func finalizeRef(v ref) - "syscall/js.finalizeRef": (v_ref) => { - // Note: TinyGo does not support finalizers so this should never be - // called. - console.error('syscall/js.finalizeRef not implemented'); - }, - - // func stringVal(value string) ref - "syscall/js.stringVal": (value_ptr, value_len) => { - const s = loadString(value_ptr, value_len); - return boxValue(s); - }, - - // func valueGet(v ref, p string) ref - "syscall/js.valueGet": (v_ref, p_ptr, p_len) => { - let prop = loadString(p_ptr, p_len); - let v = unboxValue(v_ref); - let result = Reflect.get(v, prop); - return boxValue(result); - }, - - // func valueSet(v ref, p string, x ref) - "syscall/js.valueSet": (v_ref, p_ptr, p_len, x_ref) => { - const v = unboxValue(v_ref); - const p = loadString(p_ptr, p_len); - const x = unboxValue(x_ref); - Reflect.set(v, p, x); - }, - - // func valueDelete(v ref, p string) - "syscall/js.valueDelete": (v_ref, p_ptr, p_len) => { - const v = unboxValue(v_ref); - const p = loadString(p_ptr, p_len); - Reflect.deleteProperty(v, p); - }, - - // func valueIndex(v ref, i int) ref - "syscall/js.valueIndex": (v_ref, i) => { - return boxValue(Reflect.get(unboxValue(v_ref), i)); - }, - - // valueSetIndex(v ref, i int, x ref) - "syscall/js.valueSetIndex": (v_ref, i, x_ref) => { - Reflect.set(unboxValue(v_ref), i, unboxValue(x_ref)); - }, - - // func valueCall(v ref, m string, args []ref) (ref, bool) - "syscall/js.valueCall": (ret_addr, v_ref, m_ptr, m_len, args_ptr, args_len, args_cap) => { - const v = unboxValue(v_ref); - const name = loadString(m_ptr, m_len); - const args = loadSliceOfValues(args_ptr, args_len, args_cap); - try { - const m = Reflect.get(v, name); - storeValue(ret_addr, Reflect.apply(m, v, args)); - mem().setUint8(ret_addr + 8, 1); - } catch (err) { - storeValue(ret_addr, err); - mem().setUint8(ret_addr + 8, 0); - } - }, - - // func valueInvoke(v ref, args []ref) (ref, bool) - "syscall/js.valueInvoke": (ret_addr, v_ref, args_ptr, args_len, args_cap) => { - try { - const v = unboxValue(v_ref); - const args = loadSliceOfValues(args_ptr, args_len, args_cap); - storeValue(ret_addr, Reflect.apply(v, undefined, args)); - mem().setUint8(ret_addr + 8, 1); - } catch (err) { - storeValue(ret_addr, err); - mem().setUint8(ret_addr + 8, 0); - } - }, - - // func valueNew(v ref, args []ref) (ref, bool) - "syscall/js.valueNew": (ret_addr, v_ref, args_ptr, args_len, args_cap) => { - const v = unboxValue(v_ref); - const args = loadSliceOfValues(args_ptr, args_len, args_cap); - try { - storeValue(ret_addr, Reflect.construct(v, args)); - mem().setUint8(ret_addr + 8, 1); - } catch (err) { - storeValue(ret_addr, err); - mem().setUint8(ret_addr+ 8, 0); - } - }, - - // func valueLength(v ref) int - "syscall/js.valueLength": (v_ref) => { - return unboxValue(v_ref).length; - }, - - // valuePrepareString(v ref) (ref, int) - "syscall/js.valuePrepareString": (ret_addr, v_ref) => { - const s = String(unboxValue(v_ref)); - const str = encoder.encode(s); - storeValue(ret_addr, str); - mem().setInt32(ret_addr + 8, str.length, true); - }, - - // valueLoadString(v ref, b []byte) - "syscall/js.valueLoadString": (v_ref, slice_ptr, slice_len, slice_cap) => { - const str = unboxValue(v_ref); - loadSlice(slice_ptr, slice_len, slice_cap).set(str); - }, - - // func valueInstanceOf(v ref, t ref) bool - "syscall/js.valueInstanceOf": (v_ref, t_ref) => { - return unboxValue(v_ref) instanceof unboxValue(t_ref); - }, - - // func copyBytesToGo(dst []byte, src ref) (int, bool) - "syscall/js.copyBytesToGo": (ret_addr, dest_addr, dest_len, dest_cap, src_ref) => { - let num_bytes_copied_addr = ret_addr; - let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable - - const dst = loadSlice(dest_addr, dest_len); - const src = unboxValue(src_ref); - if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) { - mem().setUint8(returned_status_addr, 0); // Return "not ok" status - return; - } - const toCopy = src.subarray(0, dst.length); - dst.set(toCopy); - mem().setUint32(num_bytes_copied_addr, toCopy.length, true); - mem().setUint8(returned_status_addr, 1); // Return "ok" status - }, - - // copyBytesToJS(dst ref, src []byte) (int, bool) - // Originally copied from upstream Go project, then modified: - // https://github.com/golang/go/blob/3f995c3f3b43033013013e6c7ccc93a9b1411ca9/misc/wasm/wasm_exec.js#L404-L416 - "syscall/js.copyBytesToJS": (ret_addr, dst_ref, src_addr, src_len, src_cap) => { - let num_bytes_copied_addr = ret_addr; - let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable - - const dst = unboxValue(dst_ref); - const src = loadSlice(src_addr, src_len); - if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) { - mem().setUint8(returned_status_addr, 0); // Return "not ok" status - return; - } - const toCopy = src.subarray(0, dst.length); - dst.set(toCopy); - mem().setUint32(num_bytes_copied_addr, toCopy.length, true); - mem().setUint8(returned_status_addr, 1); // Return "ok" status - }, - } - }; - - // Go 1.20 uses 'env'. Go 1.21 uses 'gojs'. - // For compatibility, we use both as long as Go 1.20 is supported. - this.importObject.env = this.importObject.gojs; - } - - async run(instance) { - this._inst = instance; - this._values = [ // JS values that Go currently has references to, indexed by reference id - NaN, - 0, - null, - true, - false, - global, - this, - ]; - this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id - this._ids = new Map(); // mapping from JS values to reference ids - this._idPool = []; // unused ids that have been garbage collected - this.exited = false; // whether the Go program has exited - - const mem = new DataView(this._inst.exports.memory.buffer) - - while (true) { - const callbackPromise = new Promise((resolve) => { - this._resolveCallbackPromise = () => { - if (this.exited) { - throw new Error("bad callback: Go program has already exited"); - } - setTimeout(resolve, 0); // make sure it is asynchronous - }; - }); - this._inst.exports._start(); - if (this.exited) { - break; - } - await callbackPromise; - } - } - - _resume() { - if (this.exited) { - throw new Error("Go program has already exited"); - } - this._inst.exports.resume(); - if (this.exited) { - this._resolveExitPromise(); - } - } - - _makeFuncWrapper(id) { - const go = this; - return function () { - const event = { id: id, this: this, args: arguments }; - go._pendingEvent = event; - go._resume(); - return event.result; - }; - } - } - - if ( - global.require && - global.require.main === module && - global.process && - global.process.versions && - !global.process.versions.electron - ) { - if (process.argv.length != 3) { - console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); - process.exit(1); - } - - const go = new Go(); - WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { - return go.run(result.instance); - }).catch((err) => { - console.error(err); - process.exit(1); - }); - } -})(); diff --git a/writer/ch_wrapper/adapter.go b/writer/ch_wrapper/adapter.go new file mode 100644 index 00000000..3ce61902 --- /dev/null +++ b/writer/ch_wrapper/adapter.go @@ -0,0 +1,206 @@ +package ch_wrapper + +import ( + "context" + "errors" + "github.com/ClickHouse/ch-go" + "github.com/ClickHouse/clickhouse-go/v2/lib/driver" + "github.com/metrico/cloki-config/config" + "strings" + "sync" +) + +// SmartDatabaseAdapter combines GeneralPurposeClient and WriteClient +type SmartDatabaseAdapter struct { + Xdsn string + DSN string + dbObject *config.ClokiBaseDataBase + database bool + generalPurposeClient IChClient + writeClient IChClient + mu sync.Mutex // To handle concurrency for close + onceGeneralClient sync.Once + onceWriteClient sync.Once + initErr error // Store initialization errors +} + +// initGeneralClient initializes the general purpose client once +func (a *SmartDatabaseAdapter) initGeneralClient(ctx context.Context) error { + a.onceGeneralClient.Do(func() { + var err error + if a.dbObject != nil { + a.generalPurposeClient, err = NewGeneralPurposeClient(ctx, a.dbObject, a.database) + } else if a.Xdsn != "" { + a.generalPurposeClient, err = NewGeneralPurposeClientWithXDSN(ctx, a.Xdsn, a.database) + } else if a.DSN != "" { + a.generalPurposeClient, err = NewGeneralPurposeClientWithDSN(ctx, a.DSN, a.database) + } + a.initErr = err + }) + return a.initErr +} + +// initWriteClient initializes the write client once +func (a *SmartDatabaseAdapter) initWriteClient(ctx context.Context) error { + a.onceWriteClient.Do(func() { + var err error + if a.dbObject != nil { + a.writeClient, err = NewWriterClient(ctx, a.dbObject, a.database) + } else if a.Xdsn != "" { + a.writeClient, err = NewWriterClientWithXDSN(ctx, a.Xdsn, a.database) + } else if a.DSN != "" { + a.writeClient, err = NewWriterClientWithDSN(ctx, a.DSN, a.database) + } + a.initErr = err + }) + return a.initErr +} + +// Ping Delegate methods to the appropriate client +func (a *SmartDatabaseAdapter) Ping(ctx context.Context) error { + if err := a.initWriteClient(ctx); err != nil { + return err + } + return a.writeClient.Ping(ctx) +} + +func (a *SmartDatabaseAdapter) Do(ctx context.Context, query ch.Query) error { + a.mu.Lock() + defer a.mu.Unlock() + if err := a.initWriteClient(ctx); err != nil { + return err + } + err := a.writeClient.Do(ctx, query) + if err != nil { + a.writeClient.Close() + a.writeClient = nil + a.onceWriteClient = sync.Once{} + } + return err +} + +func (a *SmartDatabaseAdapter) Exec(ctx context.Context, query string, args ...any) error { + if err := a.initGeneralClient(ctx); err != nil { + return err + } + return a.generalPurposeClient.Exec(ctx, query, args...) +} + +func (a *SmartDatabaseAdapter) Scan(ctx context.Context, req string, args []any, dest ...interface{}) error { + if err := a.initGeneralClient(ctx); err != nil { + return err + } + + return a.generalPurposeClient.Scan(ctx, req, args, dest...) +} + +// GetVersion Implement the GetVersion method in the adapter +func (a *SmartDatabaseAdapter) GetVersion(ctx context.Context, k uint64) (uint64, error) { + if err := a.initGeneralClient(ctx); err != nil { + return 0, err + } + + return a.generalPurposeClient.GetVersion(ctx, k) +} + +// TableExists Implement the TableExists method in the adapter +func (a *SmartDatabaseAdapter) TableExists(ctx context.Context, name string) (bool, error) { + if err := a.initGeneralClient(ctx); err != nil { + return false, err + } + return a.generalPurposeClient.TableExists(ctx, name) +} + +// DropIfEmpty Implement the DropIfEmpty method in the adapter +func (a *SmartDatabaseAdapter) DropIfEmpty(ctx context.Context, name string) error { + if err := a.initGeneralClient(ctx); err != nil { + return err + } + + return a.generalPurposeClient.DropIfEmpty(ctx, name) + +} + +// GetDBExec Implement the GetDBExec method in the adapter +func (a *SmartDatabaseAdapter) GetDBExec(env map[string]string) func(ctx context.Context, query string, args ...[]interface{}) error { + if err := a.initGeneralClient(context.Background()); err != nil { + return nil + } + return a.generalPurposeClient.GetDBExec(env) +} + +// GetFirst Implement the GetFirst method in the adapter +func (a *SmartDatabaseAdapter) GetFirst(req string, first ...interface{}) error { + if err := a.initGeneralClient(context.Background()); err != nil { + return err + } + + return a.generalPurposeClient.GetFirst(req, first...) +} + +// GetList Implement the GetList method in the adapter +func (a *SmartDatabaseAdapter) GetList(req string) ([]string, error) { + if err := a.initGeneralClient(context.Background()); err != nil { + return nil, err + } + return a.generalPurposeClient.GetList(req) + +} + +// Close Implement the Close method in the adapter +func (a *SmartDatabaseAdapter) Close() error { + a.mu.Lock() + defer a.mu.Unlock() + var errs []error + if a.generalPurposeClient != nil { + errs = append(errs, a.generalPurposeClient.Close()) + a.onceGeneralClient = sync.Once{} + a.generalPurposeClient = nil + } + if a.writeClient != nil { + errs = append(errs, a.writeClient.Close()) + a.onceWriteClient = sync.Once{} + a.writeClient = nil + } + var strErrs []string + for _, err := range errs { + if err != nil { + strErrs = append(strErrs, err.Error()) + } + } + if len(strErrs) > 0 { + return errors.New(strings.Join(strErrs, "; ")) + } + return nil +} + +// GetSetting Implement the GetSetting method in the adapter +func (a *SmartDatabaseAdapter) GetSetting(ctx context.Context, tp string, name string) (string, error) { + if err := a.initGeneralClient(context.Background()); err != nil { + return "", err + } + + return a.generalPurposeClient.GetSetting(ctx, tp, name) +} + +// PutSetting Implement the PutSetting method in the adapter +func (a *SmartDatabaseAdapter) PutSetting(ctx context.Context, tp string, name string, value string) error { + if err := a.initGeneralClient(ctx); err != nil { + return err + } + return a.generalPurposeClient.PutSetting(ctx, tp, name, value) +} + +func (a *SmartDatabaseAdapter) Query(ctx context.Context, query string, args ...interface{}) (driver.Rows, error) { + if err := a.initGeneralClient(ctx); err != nil { + return nil, err + } + return a.generalPurposeClient.Query(ctx, query, args...) +} + +func (a *SmartDatabaseAdapter) QueryRow(ctx context.Context, query string, args ...interface{}) driver.Row { + if err := a.initGeneralClient(ctx); err != nil { + return nil + } + return a.generalPurposeClient.QueryRow(ctx, query, args...) +} diff --git a/writer/ch_wrapper/factory.go b/writer/ch_wrapper/factory.go new file mode 100644 index 00000000..c1b56eb0 --- /dev/null +++ b/writer/ch_wrapper/factory.go @@ -0,0 +1,242 @@ +package ch_wrapper + +import ( + "context" + "crypto/tls" + "fmt" + "github.com/ClickHouse/ch-go" + "github.com/ClickHouse/clickhouse-go/v2" + "github.com/metrico/cloki-config/config" + "github.com/metrico/qryn/writer/utils/logger" + "strings" + "time" +) + +func NewGeneralPurposeClient(ctx context.Context, dbObject *config.ClokiBaseDataBase, database bool) (IChClient, error) { + logger.Info(fmt.Sprintf("Connecting to Host (SQ): [%s], User:[%s], Name:[%s], Node:[%s], Port:[%d], Timeout: [%d, %d]\n", + dbObject.Host, dbObject.User, dbObject.Name, dbObject.Node, + dbObject.Port, dbObject.ReadTimeout, dbObject.WriteTimeout)) + databaseName := "" + if database { + databaseName = dbObject.Name + } + + opt := &clickhouse.Options{ + Addr: []string{fmt.Sprintf("%s:%d", dbObject.Host, dbObject.Port)}, + Auth: clickhouse.Auth{ + Database: databaseName, + Username: dbObject.User, + Password: dbObject.Password, + }, + MaxIdleConns: dbObject.MaxIdleConn, + MaxOpenConns: dbObject.MaxOpenConn, + Compression: &clickhouse.Compression{ + Method: clickhouse.CompressionLZ4, + }, + Settings: clickhouse.Settings{ + "max_execution_time": 60, + }, + DialTimeout: 5 * time.Second, + } + if dbObject.Secure { + opt.TLS = &tls.Config{ + InsecureSkipVerify: dbObject.InsecureSkipVerify, + } + } + conn, err := clickhouse.Open(opt) + if err != nil { + return nil, err + } + + err = conn.Ping(ctx) + if err != nil { + return nil, fmt.Errorf("could not initialize clickhouse connection: %v", err) + } + + return &Client{ + c: conn, + }, nil +} + +func NewGeneralPurposeClientWithXDSN(ctx context.Context, Xdsn string, database bool) (IChClient, error) { + // Extract the prefix (n- or c-) from the X-DSN + if len(Xdsn) < 2 { + return nil, fmt.Errorf("invalid X-DSN format: %s", Xdsn) + } + dsn := Xdsn[2:] // The rest is the actual ClickHouse DSN + + return NewGeneralPurposeClientWithDSN(ctx, dsn, database) +} + +// NewGeneralPurposeClientWithDSN initializes a ClickHouse client using a string. +func NewGeneralPurposeClientWithDSN(ctx context.Context, dsn string, database bool) (IChClient, error) { + + dsnOpts, err := clickhouse.ParseDSN(dsn) + if err != nil { + return nil, err + } + + dsnOpts.Compression = &clickhouse.Compression{ + Method: clickhouse.CompressionLZ4, + } + + dsnOpts.Settings = clickhouse.Settings{ + "max_execution_time": 60, + } + + // Establish the connection + conn, err := clickhouse.Open(dsnOpts) + if err != nil { + return nil, fmt.Errorf("could not connect to ClickHouse with DSN %s: %v", dsn, err) + } + + // Ping to verify the connection + err = conn.Ping(ctx) + if err != nil { + return nil, fmt.Errorf("could not ping ClickHouse: %v", err) + } + + // Return the ClickHouse client implementing the IChClient interface + return &Client{ + c: conn, + }, nil + +} + +func NewWriterClient(ctx context.Context, dbObject *config.ClokiBaseDataBase, database bool) (IChClient, error) { + to, _ := context.WithTimeout(context.Background(), time.Second*30) + db := "" + if database { + db = dbObject.Name + } + opts := ch.Options{ + Address: fmt.Sprintf("%s:%d", dbObject.Host, dbObject.Port), + Database: db, + User: dbObject.User, + Password: dbObject.Password, + DialTimeout: time.Second * 30, + } + if dbObject.Secure { + opts.Dialer = &v3SecureDialer{dbObject.InsecureSkipVerify} + } + DSN := "n-" + if dbObject.ClusterName != "" { + DSN = "c-" + } + DSN += fmt.Sprintf("clickhouse://%s:%d/%s?secure=", dbObject.Host, dbObject.Port, db) + if dbObject.Secure { + DSN += "true" + } else { + DSN += "false" + } + client, err := ch.Dial(to, opts) + if err != nil { + return nil, fmt.Errorf("%s: %v", DSN, err) + } + err = client.Ping(context.Background()) + if err != nil { + client.Close() + return nil, err + } + + return &InsertCHWrapper{ + Client: client, + }, nil +} + +// NewWriterClientWithXDSN initializes a ClickHouse client using an X-DSN string. +func NewWriterClientWithXDSN(ctx context.Context, Xdsn string, database bool) (IChClient, error) { + + // Extract the prefix (n- or c-) from the X-DSN + if len(Xdsn) < 2 { + return nil, fmt.Errorf("invalid X-DSN format: %s", Xdsn) + } + dsn := Xdsn[2:] // The rest is the actual ClickHouse DSN + + return NewWriterClientWithDSN(ctx, dsn, database) + +} + +// NewWriterClientWithDSN initializes a ClickHouse client using a string. +func NewWriterClientWithDSN(ctx context.Context, dsn string, database bool) (IChClient, error) { + to, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + + // Parse the DSN string + dsnOpts, err := clickhouse.ParseDSN(dsn) + if err != nil { + return nil, fmt.Errorf("failed to parse DSN: %v", err) + } + + addresses := strings.Join(dsnOpts.Addr, ",") + + // Prepare ClickHouse connection options + opts := ch.Options{ + Address: addresses, // This is a slice of addresses, supporting multiple hosts for load-balancing and failover + Database: dsnOpts.Auth.Database, + User: dsnOpts.Auth.Username, + Password: dsnOpts.Auth.Password, + } + + // Add TLS configuration if present in DSN + if dsnOpts.TLS != nil { + opts.Dialer = &tls.Dialer{ + Config: &tls.Config{ + InsecureSkipVerify: dsnOpts.TLS.InsecureSkipVerify, + }, + } + } + + // Establish connection using the ch-go library + client, err := ch.Dial(to, opts) + if err != nil { + return nil, fmt.Errorf("failed to open connection: %v", err) + } + + // Ping to verify the connection + err = client.Ping(to) + if err != nil { + return nil, fmt.Errorf("failed to ping ClickHouse: %v", err) + } + + // Return the client wrapper with the original DSN + return &InsertCHWrapper{ + Client: client, + }, nil +} + +// NewSmartDatabaseAdapter initializes a SmartDatabaseAdapter using the given ClokiBaseDataBase config object. +func NewSmartDatabaseAdapter(dbObject *config.ClokiBaseDataBase, database bool) (IChClient, error) { + + if dbObject == nil { + return nil, fmt.Errorf("dbObject cannot be nil") + } + return &SmartDatabaseAdapter{ + dbObject: dbObject, + database: database, + }, nil +} + +// NewSmartDatabaseAdapterWithXDSN initializes a SmartDatabaseAdapter using an X-DSN string. +func NewSmartDatabaseAdapterWithXDSN(Xdsn string, database bool) (IChClient, error) { + if Xdsn == "" { + return nil, fmt.Errorf("X-DSN cannot be empty") + } + + return &SmartDatabaseAdapter{ + Xdsn: Xdsn, + database: database, + }, nil +} + +// NewSmartDatabaseAdapterWithDSN initializes a SmartDatabaseAdapter using a ClickHouse DSN string. +func NewSmartDatabaseAdapterWithDSN(dsn string, database bool) (IChClient, error) { + + if dsn == "" { + return nil, fmt.Errorf("DSN cannot be empty") + } + return &SmartDatabaseAdapter{ + DSN: dsn, + database: database, + }, nil +} diff --git a/writer/ch_wrapper/general_purpose_ch_client.go b/writer/ch_wrapper/general_purpose_ch_client.go new file mode 100644 index 00000000..8af61c0c --- /dev/null +++ b/writer/ch_wrapper/general_purpose_ch_client.go @@ -0,0 +1,234 @@ +package ch_wrapper + +import ( + "bytes" + "context" + "fmt" + "github.com/ClickHouse/ch-go" + "github.com/ClickHouse/clickhouse-go/v2/lib/driver" + "github.com/metrico/qryn/writer/utils/heputils" + "github.com/metrico/qryn/writer/utils/logger" + rand2 "math/rand" + "strconv" + "text/template" + "time" +) + +type Client struct { + c driver.Conn +} + +var _ IChClient = &Client{} + +func (c *Client) Scan(ctx context.Context, req string, args []any, dest ...interface{}) error { + rows, err := c.c.Query(ctx, req, args...) + if err != nil { + return err + } + defer func(rows driver.Rows) { + err := rows.Close() + if err != nil { + logger.Error(err) + } + }(rows) + for rows.Next() { + err = rows.Scan(dest...) + if err != nil { + return err + } + } + return nil +} + +func (c *Client) DropIfEmpty(ctx context.Context, name string) error { + exists, err := c.TableExists(ctx, name) + if err != nil { + return err + } + if !exists { + return nil + } + empty, err := c.tableEmpty(ctx, name) + if err != nil { + return err + } + if !empty { + return nil + } + err = c.c.Exec(ctx, fmt.Sprintf("DROP TABLE IF EXISTS %s", name)) + return err +} + +func (c *Client) GetVersion(ctx context.Context, k uint64) (uint64, error) { + rows, err := c.c.Query(ctx, "SELECT max(ver) as ver FROM ver WHERE k = $1 FORMAT JSON", k) + if err != nil { + return 0, err + } + var ver uint64 = 0 + for rows.Next() { + err = rows.Scan(&ver) + if err != nil { + return 0, err + } + } + return ver, nil +} + +func (c *Client) TableExists(ctx context.Context, name string) (bool, error) { + rows, err := c.c.Query(ctx, "SHOW TABLES") + if err != nil { + return false, err + } + defer func(rows driver.Rows) { + err := rows.Close() + if err != nil { + logger.Error(err) + } + }(rows) + for rows.Next() { + var _name string + err = rows.Scan(&_name) + if err != nil { + return false, err + } + if _name == name { + return true, nil + } + } + return false, nil +} + +func (c *Client) tableEmpty(ctx context.Context, name string) (bool, error) { + rows, err := c.c.Query(ctx, fmt.Sprintf("SELECT count(1) FROM %s", name)) + if err != nil { + return false, err + } + defer func(rows driver.Rows) { + err := rows.Close() + if err != nil { + logger.Error(err) + } + }(rows) + rows.Next() + var count uint64 + err = rows.Scan(&count) + return count == 0, err +} + +func (c *Client) Exec(ctx context.Context, query string, args ...any) error { + + logger.Info("query Info", query) + return c.c.Exec(ctx, query, args) +} + +func (c *Client) GetDBExec(env map[string]string) func(ctx context.Context, query string, args ...[]interface{}) error { + rand := rand2.New(rand2.NewSource(time.Now().UnixNano())) + return func(ctx context.Context, query string, args ...[]interface{}) error { + name := fmt.Sprintf("tpl_%d", rand.Uint64()) + tpl, err := template.New(name).Parse(query) + if err != nil { + logger.Error(query) + return err + } + buf := bytes.NewBuffer(nil) + err = tpl.Execute(buf, env) + if err != nil { + logger.Error(query) + return err + } + req := buf.String() + logger.Info(req) + err = c.c.Exec(ctx, req) + if err != nil { + logger.Error(req) + return err + } + return nil + } +} + +func (c *Client) GetFirst(req string, first ...interface{}) error { + res, err := c.c.Query(context.Background(), req) + if err != nil { + return err + } + defer res.Close() + res.Next() + err = res.Scan(first...) + return err +} + +func (c *Client) GetList(req string) ([]string, error) { + res, err := c.c.Query(context.Background(), req) + if err != nil { + logger.Error("GetList Error", err.Error()) + return nil, err + } + defer res.Close() + arr := make([]string, 0) + for res.Next() { + var val string + err = res.Scan(&val) + if err != nil { + logger.Error("GetList Error", err.Error()) + return nil, err + } + arr = append(arr, val) + } + return arr, nil +} + +func (c *Client) Close() error { + return c.c.Close() +} + +func (c *Client) GetSetting(ctx context.Context, tp string, name string) (string, error) { + fp := heputils.FingerprintLabelsDJBHashPrometheus([]byte( + fmt.Sprintf(`{"type":%s, "name":%s`, strconv.Quote(tp), strconv.Quote(name)), + )) + rows, err := c.c.Query(ctx, `SELECT argMax(value, inserted_at) as _value FROM settings WHERE fingerprint = $1 +GROUP BY fingerprint HAVING argMax(name, inserted_at) != ''`, fp) + if err != nil { + return "", err + } + res := "" + for rows.Next() { + err = rows.Scan(&res) + if err != nil { + return "", err + } + } + return res, nil +} + +func (c *Client) PutSetting(ctx context.Context, tp string, name string, value string) error { + _name := fmt.Sprintf(`{"type":%s, "name":%s`, strconv.Quote(tp), strconv.Quote(name)) + fp := heputils.FingerprintLabelsDJBHashPrometheus([]byte(_name)) + err := c.c.Exec(ctx, `INSERT INTO settings (fingerprint, type, name, value, inserted_at) +VALUES ($1, $2, $3, $4, NOW())`, fp, tp, name, value) + return err +} + +func (c *Client) Ping(ctx context.Context) error { + //TODO implement me + panic("implement me") +} + +func (c *Client) Do(ctx context.Context, query ch.Query) error { + //TODO implement me + panic("implement me") +} + +func (c *Client) Query(ctx context.Context, query string, args ...interface{}) (driver.Rows, error) { + // Call the ClickHouse Query method on the connection object with the provided query and arguments + rows, err := c.c.Query(ctx, query, args...) + if err != nil { + return nil, err + } + return rows, nil +} + +func (c *Client) QueryRow(ctx context.Context, query string, args ...interface{}) driver.Row { + // Call the QueryRow method from the underlying ClickHouse connection + return c.c.QueryRow(ctx, query, args...) +} diff --git a/writer/ch_wrapper/insert_ch_client.go b/writer/ch_wrapper/insert_ch_client.go new file mode 100644 index 00000000..6aea3402 --- /dev/null +++ b/writer/ch_wrapper/insert_ch_client.go @@ -0,0 +1,98 @@ +package ch_wrapper + +import ( + "context" + "crypto/tls" + "errors" + "github.com/ClickHouse/ch-go" + "github.com/ClickHouse/clickhouse-go/v2/lib/driver" + "net" + "sync" +) + +type InsertCHWrapper struct { + mutex sync.Mutex + *ch.Client +} + +var _ IChClient = &InsertCHWrapper{} + +func (c *InsertCHWrapper) Query(ctx context.Context, query string, args ...interface{}) (driver.Rows, error) { + //TODO implement me + panic("implement me") +} + +func (c *InsertCHWrapper) QueryRow(ctx context.Context, query string, args ...interface{}) driver.Row { + //TODO implement me + panic("implement me") +} + +func (c *InsertCHWrapper) Do(ctx context.Context, query ch.Query) error { + c.mutex.Lock() + defer c.mutex.Unlock() + return c.Client.Do(ctx, query) +} + +func (c *InsertCHWrapper) Ping(ctx context.Context) error { + c.mutex.Lock() + defer c.mutex.Unlock() + return c.Client.Ping(ctx) +} + +func (c *InsertCHWrapper) Close() error { + c.mutex.Lock() + defer c.mutex.Unlock() + return c.Client.Close() +} + +type v3SecureDialer struct { + InsecureSkipVerify bool +} + +func (v *v3SecureDialer) DialContext(_ context.Context, network string, address string) (net.Conn, error) { + return tls.Dial(network, address, &tls.Config{InsecureSkipVerify: v.InsecureSkipVerify}) +} + +// Methods that should return errors in InsertClient +func (c *InsertCHWrapper) Exec(ctx context.Context, query string, args ...any) error { + //return c.Exec(ctx, query, args) + return errors.New("not implemented") +} + +func (c *InsertCHWrapper) Scan(ctx context.Context, req string, args []any, dest ...interface{}) error { + return errors.New("not implemented") +} + +func (c *InsertCHWrapper) DropIfEmpty(ctx context.Context, name string) error { + return errors.New("not implemented") +} + +func (c *InsertCHWrapper) TableExists(ctx context.Context, name string) (bool, error) { + return false, errors.New("not implemented") +} + +func (c *InsertCHWrapper) GetDBExec(env map[string]string) func(ctx context.Context, query string, args ...[]interface{}) error { + return func(ctx context.Context, query string, args ...[]interface{}) error { + return errors.New("not implemented") + } +} + +func (c *InsertCHWrapper) GetVersion(ctx context.Context, k uint64) (uint64, error) { + return 0, errors.New("not implemented") +} + +func (c *InsertCHWrapper) GetSetting(ctx context.Context, tp string, name string) (string, error) { + return "", errors.New("not implemented") +} + +func (c *InsertCHWrapper) PutSetting(ctx context.Context, tp string, name string, value string) error { + return errors.New("not implemented") +} + +func (c *InsertCHWrapper) GetFirst(req string, first ...interface{}) error { + return errors.New("not implemented") +} + +func (c *InsertCHWrapper) GetList(req string) ([]string, error) { + return nil, errors.New("not implemented") +} diff --git a/writer/ch_wrapper/types.go b/writer/ch_wrapper/types.go new file mode 100644 index 00000000..ae74cb8d --- /dev/null +++ b/writer/ch_wrapper/types.go @@ -0,0 +1,34 @@ +package ch_wrapper + +import ( + "context" + "github.com/ClickHouse/ch-go" + "github.com/ClickHouse/clickhouse-go/v2/lib/driver" +) + +// We combine both ch clients: adapter.Client and main.chWrapper in one interface + +type IChClient interface { + // This should be implemented in the insert client and return error in the general purpose client + Ping(ctx context.Context) error + Do(ctx context.Context, query ch.Query) error + + // This should be implemented in the general purpose client and return error in the insert one + Exec(ctx context.Context, query string, args ...any) error + Scan(ctx context.Context, req string, args []any, dest ...interface{}) error + DropIfEmpty(ctx context.Context, name string) error + TableExists(ctx context.Context, name string) (bool, error) + GetDBExec(env map[string]string) func(ctx context.Context, query string, args ...[]interface{}) error + GetVersion(ctx context.Context, k uint64) (uint64, error) + GetSetting(ctx context.Context, tp string, name string) (string, error) + PutSetting(ctx context.Context, tp string, name string, value string) error + GetFirst(req string, first ...interface{}) error + GetList(req string) ([]string, error) + Query(ctx context.Context, query string, args ...interface{}) (driver.Rows, error) + QueryRow(ctx context.Context, query string, args ...interface{}) driver.Row + // This one is shared by both + Close() error +} + +// TODO: think about the factory type +type IChClientFactory func() (IChClient, error) diff --git a/writer/ch_wrapper/unit_test.go b/writer/ch_wrapper/unit_test.go new file mode 100644 index 00000000..6dba0d64 --- /dev/null +++ b/writer/ch_wrapper/unit_test.go @@ -0,0 +1,118 @@ +package ch_wrapper + +import ( + "context" + "fmt" + "github.com/ClickHouse/ch-go" + config2 "github.com/metrico/cloki-config/config" + "github.com/stretchr/testify/assert" + "os" + "strconv" + "testing" +) + +func TestNewInsertCHClient(t *testing.T) { + if os.Getenv("CLICKHOUSE_HOST") == "" { + return + } + port, err := strconv.Atoi(os.Getenv("CLICKHOUSE_PORT")) + assert.NoError(t, err) + ctx := context.Background() + cfg := config2.ClokiBaseDataBase{ + User: os.Getenv("CLICKHOUSE_USER"), + Node: "ch", + Password: os.Getenv("CLICKHOUSE_PASSWORD"), + Name: os.Getenv("CLICKHOUSE_DATABASE"), + Host: os.Getenv("CLICKHOUSE_HOST"), + Port: uint32(port), + } + + smartAdapter, err := NewSmartDatabaseAdapter(&cfg, true) + assert.NoError(t, err) + assert.NotNil(t, smartAdapter) + var res uint8 + err = smartAdapter.GetFirst("SELECT 1", &res) + assert.NoError(t, err) + fmt.Println(res) + + createTableQuery := "CREATE TABLE IF NOT EXISTS exp (a UInt8) ENGINE=MergeTree ORDER BY ()" + err = smartAdapter.Exec(ctx, createTableQuery) + assert.NoError(t, err) + + err = smartAdapter.Do(ctx, ch.Query{ + Body: "INSERT INTO exp (a) VALUES (1)", + }) + + assert.NoError(t, err) + + // 4. Test Select count() from the table + var count uint64 + err = smartAdapter.Scan(ctx, "SELECT count() FROM exp", nil, &count) + assert.NoError(t, err) + fmt.Println(count) +} + +func TestNewInsertCHClientXDSN(t *testing.T) { + ctx := context.Background() + Xdsn := os.Getenv("CLICKHOUSE_XDSN") + if Xdsn == "" { + fmt.Println("CLICKHOUSE_DSN environment variable is not set") + return + } + smartAdapter, err := NewSmartDatabaseAdapterWithXDSN(Xdsn, true) + assert.NoError(t, err) + assert.NotNil(t, smartAdapter) + // 1. Test Select 1 using the General Purpose Client + err = smartAdapter.Exec(ctx, "SELECT 1") + assert.NoError(t, err) + + // 2. Test Create Table + createTableQuery := "CREATE TABLE IF NOT EXISTS exp (a UInt8) ENGINE=MergeTree ORDER BY ()" + err = smartAdapter.Exec(ctx, createTableQuery) + assert.NoError(t, err) + + // 3. Test Insert a row into the table + err = smartAdapter.Do(ctx, ch.Query{ + Body: "INSERT INTO exp (a) VALUES (1)", + }) + assert.NoError(t, err) + + // 4. Test Select count() from the table + var count uint64 + err = smartAdapter.Scan(ctx, "SELECT count() FROM exp", nil, &count) + assert.NoError(t, err) + fmt.Println(count) +} + +func TestNewInsertCHClientWithOutDSN(t *testing.T) { + ctx := context.Background() + dsn := os.Getenv("CLICKHOUSE_DSN") + if dsn == "" { + fmt.Println("CLICKHOUSE_DSN environment variable is not set") + return + } + smartAdapter, err := NewSmartDatabaseAdapterWithDSN(dsn, true) + assert.NoError(t, err) + assert.NotNil(t, smartAdapter) + + // 1. Test Select 1 using the General Purpose Client + err = smartAdapter.Exec(ctx, "SELECT 1") + assert.NoError(t, err) + + // 2. Test Create Table + createTableQuery := "CREATE TABLE IF NOT EXISTS exp (a UInt8) ENGINE=MergeTree ORDER BY ()" + err = smartAdapter.Exec(ctx, createTableQuery) + assert.NoError(t, err) + + // 3. Test Insert a row into the table + err = smartAdapter.Do(ctx, ch.Query{ + Body: "INSERT INTO exp (a) VALUES (1)", + }) + assert.NoError(t, err) + + // 4. Test Select count() from the table + var count uint64 + err = smartAdapter.Scan(ctx, "SELECT count() FROM exp", nil, &count) + assert.NoError(t, err) + fmt.Println(count) +} diff --git a/writer/config/config.go b/writer/config/config.go new file mode 100644 index 00000000..b3fed345 --- /dev/null +++ b/writer/config/config.go @@ -0,0 +1,8 @@ +package config + +import ( + clconfig "github.com/metrico/cloki-config" +) + +var NAME_APPLICATION = "cloki-writer" +var Cloki *clconfig.ClokiConfig diff --git a/writer/controller/builder.go b/writer/controller/builder.go new file mode 100644 index 00000000..2aea574a --- /dev/null +++ b/writer/controller/builder.go @@ -0,0 +1,233 @@ +package controllerv1 + +import ( + "context" + "encoding/json" + retry "github.com/avast/retry-go" + "github.com/metrico/qryn/writer/config" + customErrors "github.com/metrico/qryn/writer/utils/errors" + "github.com/metrico/qryn/writer/utils/helpers" + "github.com/metrico/qryn/writer/utils/logger" + "github.com/metrico/qryn/writer/utils/promise" + "github.com/metrico/qryn/writer/utils/stat" + "io" + "net/http" + "strings" + "time" + + "github.com/metrico/qryn/writer/model" + "github.com/metrico/qryn/writer/service" + "github.com/metrico/qryn/writer/utils/numbercache" +) + +const MaxRetries = 10 +const RetrySleepTimeS = 30 + +type MiddlewareConfig struct { + ExtraMiddleware []BuildOption +} + +// NewMiddlewareConfig generates a MiddlewareConfig from given middleware constructors. +func NewMiddlewareConfig(middlewares ...BuildOption) MiddlewareConfig { + return MiddlewareConfig{ + ExtraMiddleware: append([]BuildOption{}, middlewares...), + } +} + +type Requester func(w http.ResponseWriter, r *http.Request) error +type Parser func(ctx context.Context, body io.Reader, fpCache numbercache.ICache[uint64]) chan *model.ParserResponse + +type BuildOption func(ctx *PusherCtx) *PusherCtx + +type PusherCtx struct { + PreRequest []Requester + PostRequest []Requester + Parser map[string]Requester + ResponseBody []byte +} + +func (pusherCtx *PusherCtx) Do(w http.ResponseWriter, r *http.Request) error { + var err error + for _, p := range pusherCtx.PreRequest { + + err = p(w, r) + if err != nil { + return err + } + } + + err = pusherCtx.DoParse(r, w) + if err != nil { + return err + } + + for _, p := range pusherCtx.PostRequest { + err = p(w, r) + if err != nil { + return err + } + } + + return nil +} + +func ErrorHandler(w http.ResponseWriter, r *http.Request, err error) { + if e, ok := customErrors.Unwrap[*customErrors.UnMarshalError](err); ok { + stat.AddSentMetrics("json_parse_errors", 1) + writeErrorResponse(w, e.GetCode(), e.Error()) + return + } + if e, ok := customErrors.Unwrap[customErrors.IQrynError](err); ok { + writeErrorResponse(w, e.GetCode(), e.Error()) + return + } + if strings.HasPrefix(err.Error(), "connection reset by peer") { + stat.AddSentMetrics("connection_reset_by_peer", 1) + return + } + logger.Error(err) + writeErrorResponse(w, http.StatusInternalServerError, "internal server error") +} +func writeErrorResponse(w http.ResponseWriter, statusCode int, message string) { + w.WriteHeader(statusCode) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]interface{}{ + "success": false, + "message": message, + }) +} + +func (pusherCtx *PusherCtx) DoParse(r *http.Request, w http.ResponseWriter) error { + if len(pusherCtx.Parser) == 0 { + return nil + } + contentType := r.Header.Get("Content-Type") + + var parser Requester + for k, p := range pusherCtx.Parser { + if strings.HasPrefix(contentType, k) { + parser = p + break + } + } + if p, ok := pusherCtx.Parser["*"]; parser == nil && ok { + parser = p + } + + if parser == nil { + return customErrors.New400Error("Content-Type not supported") + } + + return parser(w, r.WithContext(r.Context())) +} + +func Build(options ...BuildOption) func(w http.ResponseWriter, r *http.Request) { + pusherCtx := &PusherCtx{ + Parser: map[string]Requester{}, + } + for _, o := range options { + pusherCtx = o(pusherCtx) + } + + // Return a function that handles request and response and also performs error handling + return func(w http.ResponseWriter, r *http.Request) { + // Execute pusherCtx.Do + err := pusherCtx.Do(w, r) + if err != nil { + ErrorHandler(w, r, err) // Call ErrorHandler if pusherCtx.Do returns an error + } + return + } + +} + +func getService(r *http.Request, name string) service.IInsertServiceV2 { + ctx := r.Context() + svc := ctx.Value(name) + if svc == nil { + return nil + } + return svc.(service.IInsertServiceV2) +} + +func doPush(req helpers.SizeGetter, insertMode int, svc service.IInsertServiceV2) *promise.Promise[uint32] { + // errChan := make(chan error, 1) + p := promise.New[uint32]() + if req == nil || svc == nil { + return promise.Fulfilled[uint32](nil, 0) + } + retryAttempts := uint(config.Cloki.Setting.SYSTEM_SETTINGS.RetryAttempts) + retryDelay := time.Duration(config.Cloki.Setting.SYSTEM_SETTINGS.RetryTimeoutS) * time.Second + // Use the retry-go library to attempt the request up to MaxRetries times. + go func() { + err := retry.Do( + func() error { + //req.ResetResponse() + reqPromise := svc.Request(req, insertMode) + _, reqErr := reqPromise.Get() // Wait for the result from the svc.Request + if reqErr != nil { + if strings.Contains(reqErr.Error(), "dial tcp: lookup") && + strings.Contains(reqErr.Error(), "i/o timeout") { + stat.AddSentMetrics("dial_tcp_lookup_timeout", 1) + } + logger.Error("Request error:", reqErr) + return reqErr + } + return nil + }, + retry.Attempts(retryAttempts), + retry.Delay(retryDelay), + retry.DelayType(retry.FixedDelay), + ) + p.Done(0, err) + if err != nil { + logger.Error("Retry failed after attempts:", err) + } + }() + return p +} +func getBodyStream(r *http.Request) io.Reader { + if bodyStream, ok := r.Context().Value("bodyStream").(io.Reader); ok { + return bodyStream + } + return r.Body +} + +func doParse(r *http.Request, parser Parser) error { + reader := getBodyStream(r) + tsService := getService(r, "tsService") + splService := getService(r, "splService") + spanAttrsService := getService(r, "spanAttrsService") + spansService := getService(r, "spansService") + profileService := getService(r, "profileService") + node := r.Context().Value("node").(string) + + //var promises []chan error + var promises []*promise.Promise[uint32] + var err error = nil + res := parser(r.Context(), reader, FPCache.DB(node)) + for response := range res { + if response.Error != nil { + go func() { + for range res { + } + }() + return response.Error + } + promises = append(promises, + doPush(response.TimeSeriesRequest, service.INSERT_MODE_SYNC, tsService), + doPush(response.SamplesRequest, service.INSERT_MODE_SYNC, splService), + doPush(response.SpansAttrsRequest, service.INSERT_MODE_SYNC, spanAttrsService), + doPush(response.SpansRequest, service.INSERT_MODE_SYNC, spansService), + doPush(response.ProfileRequest, service.INSERT_MODE_SYNC, profileService), + ) + + } + for _, p := range promises { + _, err = p.Get() + if err != nil { + return err + } + } + return nil +} diff --git a/writer/controller/datadogController.go b/writer/controller/datadogController.go new file mode 100644 index 00000000..624f4641 --- /dev/null +++ b/writer/controller/datadogController.go @@ -0,0 +1,77 @@ +package controllerv1 + +import ( + "context" + "github.com/metrico/qryn/writer/utils/unmarshal" + "net/http" +) + +func PushDatadogV2(cfg MiddlewareConfig) func(w http.ResponseWriter, r *http.Request) { + return Build( + append(cfg.ExtraMiddleware, + withTSAndSampleService, + withParserContext(func(w http.ResponseWriter, req *http.Request, parserCtx context.Context) (context.Context, error) { + + ddsource := req.URL.Query().Get("ddsource") + if ddsource == "" { + ddsource = "unknown" + } + return context.WithValue(parserCtx, "ddsource", ddsource), nil + }), + withSimpleParser("application/json", Parser(unmarshal.UnmarshallDatadogV2JSONV2)), + withOkStatusAndBody(202, []byte("{}")))...) +} + +//var PushDatadogV2 = Build( +// append(WithExtraMiddlewareDefault, +// withTSAndSampleService, +// withSimpleParser("application/json", Parser(unmarshal.UnmarshallDatadogV2JSONV2)), +// withOkStatusAndBody(202, []byte("ok")), +// )..., +//) + +func PushCfDatadogV2(cfg MiddlewareConfig) func(w http.ResponseWriter, r *http.Request) { + return Build( + append(cfg.ExtraMiddleware, + withTSAndSampleService, + withParserContext(func(w http.ResponseWriter, req *http.Request, parserCtx context.Context) (context.Context, error) { + ddsource := req.URL.Query().Get("ddsource") + if ddsource == "" { + ddsource = "unknown" + } + return context.WithValue(parserCtx, "ddsource", ddsource), nil + }), + withSimpleParser("*", Parser(unmarshal.UnmarshallDatadogCFJSONV2)), + withOkStatusAndBody(202, []byte("{}")))...) +} + +//var PushCfDatadogV2 = Build( +// append(WithExtraMiddlewareDefault, +// withTSAndSampleService, +// withParserContext(func(w http.ResponseWriter, req *http.Request, parserCtx context.Context) (context.Context, error) { +// +// ddsource := req.URL.Query().Get("ddsource") +// if ddsource == "" { +// ddsource = "unknown" +// } +// return context.WithValue(parserCtx, "ddsource", ddsource), nil +// }), +// withSimpleParser("*", Parser(unmarshal.UnmarshallDatadogCFJSONV2)), +// withOkStatusAndBody(200, []byte("{}")))...) + +func PushDatadogMetricsV2(cfg MiddlewareConfig) func(w http.ResponseWriter, r *http.Request) { + + return Build( + append(cfg.ExtraMiddleware, + withTSAndSampleService, + withSimpleParser("application/json", Parser(unmarshal.UnmarshallDatadogMetricsV2JSONV2)), + withOkStatusAndBody(202, []byte("{}")))...) + +} + +// +//var PushDatadogMetricsV2 = Build( +// append(WithExtraMiddlewareDefault, +// withTSAndSampleService, +// withSimpleParser("application/json", Parser(unmarshal.UnmarshallDatadogMetricsV2JSONV2)), +// withOkStatusAndBody(202, []byte("{}")))...) diff --git a/writer/controller/dev.go b/writer/controller/dev.go new file mode 100644 index 00000000..89a7e64f --- /dev/null +++ b/writer/controller/dev.go @@ -0,0 +1,8 @@ +package controllerv1 + +var WithExtraMiddlewareDefault = []BuildOption{ + WithOverallContextMiddleware, +} +var WithExtraMiddlewareTempo = []BuildOption{ + WithOverallContextMiddleware, +} diff --git a/writer/controller/elasticController.go b/writer/controller/elasticController.go new file mode 100644 index 00000000..afee4e6a --- /dev/null +++ b/writer/controller/elasticController.go @@ -0,0 +1,143 @@ +package controllerv1 + +import ( + "context" + "encoding/json" + "github.com/metrico/qryn/writer/utils/unmarshal" + "net/http" + "strings" +) + +func TargetDocV2(cfg MiddlewareConfig) func(w http.ResponseWriter, r *http.Request) { + return Build( + append(cfg.ExtraMiddleware, + withTSAndSampleService, + withParserContext(func(w http.ResponseWriter, req *http.Request, parserCtx context.Context) (context.Context, error) { + params := getRequestParams(req) + // Access individual parameter values + target := params["target"] + id := params["id"] + firstSlash := strings.Index(target, "/") + if firstSlash != -1 { + target = target[:firstSlash] + } + _ctx := context.WithValue(parserCtx, "target", target) + _ctx = context.WithValue(_ctx, "id", id) + return _ctx, nil + }), + withSimpleParser("*", Parser(unmarshal.ElasticDocUnmarshalV2)), + withOkStatusAndJSONBody(200, map[string]interface{}{ + "took": 0, + "errors": false, + }))...) +} + +//var ( +// TargetDocV2 = Build( +// append(WithExtraMiddlewareDefault, +// withTSAndSampleService, +// withParserContext(func(w http.ResponseWriter, req *http.Request, parserCtx context.Context) (context.Context, error) { +// params := getRequestParams(req) +// // Access individual parameter values +// target := params["target"] +// id := params["id"] +// firstSlash := strings.Index(target, "/") +// if firstSlash != -1 { +// target = target[:firstSlash] +// } +// _ctx := context.WithValue(parserCtx, "target", target) +// _ctx = context.WithValue(_ctx, "id", id) +// return _ctx, nil +// }), +// withSimpleParser("*", Parser(unmarshal.ElasticDocUnmarshalV2)), +// withOkStatusAndJSONBody(200, map[string]interface{}{ +// "took": 0, +// "errors": false, +// }))...) +//) + +func TargetBulkV2(cfg MiddlewareConfig) func(w http.ResponseWriter, r *http.Request) { + return Build(append(cfg.ExtraMiddleware, + withTSAndSampleService, + withParserContext(func(w http.ResponseWriter, req *http.Request, parserCtx context.Context) (context.Context, error) { + params := getRequestParams(req) + // Access individual parameter values + target := params["target"] + _ctx := context.WithValue(parserCtx, "target", target) + return _ctx, nil + }), + withSimpleParser("*", Parser(unmarshal.ElasticBulkUnmarshalV2)), + withPostRequest(func(w http.ResponseWriter, r *http.Request) error { + w.Header().Set("x-elastic-product", "Elasticsearch") + // Set response status code + w.WriteHeader(http.StatusOK) + // Prepare JSON response data + responseData := map[string]interface{}{ + "took": 0, + "errors": false, + } + // Marshal JSON response data + responseJSON, err := json.Marshal(responseData) + if err != nil { + // If an error occurs during JSON marshaling, return an internal server error + http.Error(w, err.Error(), http.StatusInternalServerError) + return err + } + // Write JSON response to the response writer + _, err = w.Write(responseJSON) + if err != nil { + // If an error occurs during writing to the response writer, return an internal server error + http.Error(w, err.Error(), http.StatusInternalServerError) + return err + } + return nil + }))...) +} + +//var TargetBulkV2 = Build( +// append(WithExtraMiddlewareDefault, +// withTSAndSampleService, +// withParserContext(func(w http.ResponseWriter, req *http.Request, parserCtx context.Context) (context.Context, error) { +// params := getRequestParams(req) +// // Access individual parameter values +// target := params["target"] +// _ctx := context.WithValue(parserCtx, "target", target) +// return _ctx, nil +// }), +// withSimpleParser("*", Parser(unmarshal.ElasticBulkUnmarshalV2)), +// withPostRequest(func(r *http.Request, w http.ResponseWriter) error { +// w.Header().Set("x-elastic-product", "Elasticsearch") +// // Set response status code +// w.WriteHeader(http.StatusOK) +// // Prepare JSON response data +// responseData := map[string]interface{}{ +// "took": 0, +// "errors": false, +// } +// // Marshal JSON response data +// responseJSON, err := json.Marshal(responseData) +// if err != nil { +// // If an error occurs during JSON marshaling, return an internal server error +// http.Error(w, err.Error(), http.StatusInternalServerError) +// return err +// } +// // Write JSON response to the response writer +// _, err = w.Write(responseJSON) +// if err != nil { +// // If an error occurs during writing to the response writer, return an internal server error +// http.Error(w, err.Error(), http.StatusInternalServerError) +// return err +// } +// return nil +// }))...) + +func getRequestParams(r *http.Request) map[string]string { + params := make(map[string]string) + ctx := r.Context() + if ctxParams, ok := ctx.Value("params").(map[string]string); ok { + for key, value := range ctxParams { + params[key] = value + } + } + return params +} diff --git a/writer/controller/global.go b/writer/controller/global.go new file mode 100644 index 00000000..818f15fb --- /dev/null +++ b/writer/controller/global.go @@ -0,0 +1,9 @@ +package controllerv1 + +import ( + "github.com/metrico/qryn/writer/service/registry" + "github.com/metrico/qryn/writer/utils/numbercache" +) + +var Registry registry.IServiceRegistry +var FPCache numbercache.ICache[uint64] diff --git a/writer/controller/insertController.go b/writer/controller/insertController.go new file mode 100644 index 00000000..c0051961 --- /dev/null +++ b/writer/controller/insertController.go @@ -0,0 +1,162 @@ +package controllerv1 + +import ( + "context" + "fmt" + custom_errors "github.com/metrico/qryn/writer/utils/errors" + "github.com/metrico/qryn/writer/utils/unmarshal" + "net/http" + "time" +) + +// swagger:route GET /push Data PushData +// +// # Returns data from server in array +// +// --- +// +// Consumes: +// - application/json +// +// Produces: +// - application/json +// +// Security: +// - JWT +// - ApiKeyAuth +// +// SecurityDefinitions: +// JWT: +// +// type: apiKey +// name: Authorization +// in: header +// +// ApiKeyAuth: +// +// type: apiKey +// in: header +// name: Auth-Token +// +// / +// +// Responses: +// 201: body:TableUserList +// 400: body:FailureResponse + +func PushStreamV2(cfg MiddlewareConfig) func(w http.ResponseWriter, r *http.Request) { + return Build( + append(cfg.ExtraMiddleware, + withTSAndSampleService, + withSimpleParser("*", Parser(unmarshal.DecodePushRequestStringV2)), + withComplexParser("application/x-protobuf", + Parser(unmarshal.UnmarshalProtoV2), + withUnsnappyRequest), + withOkStatusAndBody(204, nil), + )..., + ) +} + +//var PushStreamV2 = Build( +// append(WithExtraMiddlewareDefault, +// withTSAndSampleService, +// withSimpleParser("*", Parser(unmarshal.DecodePushRequestStringV2)), +// withComplexParser("application/x-protobuf", +// Parser(unmarshal.UnmarshalProtoV2), +// withUnsnappyRequest), +// withOkStatusAndBody(204, nil), +// )...) + +func PushInfluxV2(cfg MiddlewareConfig) func(w http.ResponseWriter, r *http.Request) { + return Build( + append(cfg.ExtraMiddleware, + withTSAndSampleService, + withParserContext(func(w http.ResponseWriter, req *http.Request, parserCtx context.Context) (context.Context, error) { + strPrecision := req.URL.Query().Get("precision") + if strPrecision == "" { + strPrecision = "ns" + } + var precision time.Duration + switch strPrecision { + case "ns": + precision = time.Nanosecond + case "us": + precision = time.Microsecond + case "ms": + precision = time.Millisecond + case "s": + precision = time.Second + default: + return nil, custom_errors.New400Error(fmt.Sprintf("Invalid precision %s", strPrecision)) + } + ctx := req.Context() + ctx = context.WithValue(ctx, "precision", precision) + return ctx, nil + }), + withSimpleParser("*", Parser(unmarshal.UnmarshalInfluxDBLogsV2)), + withPostRequest(func(w http.ResponseWriter, r *http.Request) error { + w.WriteHeader(http.StatusNoContent) + // Write "Ok" as the response body + _, _ = w.Write([]byte("Ok")) + + return nil + }))...) +} + +//var PushInfluxV2 = Build( +// append(WithExtraMiddlewareDefault, +// withTSAndSampleService, +// withParserContext(func(w http.ResponseWriter, req *http.Request, parserCtx context.Context) (context.Context, error) { +// strPrecision := req.URL.Query().Get("precision") +// if strPrecision == "" { +// strPrecision = "ns" +// } +// var precision time.Duration +// switch strPrecision { +// case "ns": +// precision = time.Nanosecond +// case "us": +// precision = time.Microsecond +// case "ms": +// precision = time.Millisecond +// case "s": +// precision = time.Second +// default: +// return nil, custom_errors.New400Error(fmt.Sprintf("Invalid precision %s", strPrecision)) +// } +// ctx := req.Context() +// ctx = context.WithValue(ctx, "precision", precision) +// return ctx, nil +// }), +// withSimpleParser("*", Parser(unmarshal.UnmarshalInfluxDBLogsV2)), +// withPostRequest(func(r *http.Request, w http.ResponseWriter) error { +// w.WriteHeader(http.StatusNoContent) +// // Write "Ok" as the response body +// _, _ = w.Write([]byte("Ok")) +// +// return nil +// }))...) + +func OTLPLogsV2(cfg MiddlewareConfig) func(w http.ResponseWriter, r *http.Request) { + return Build( + append(cfg.ExtraMiddleware, + withTSAndSampleService, + withSimpleParser("*", Parser(unmarshal.UnmarshalOTLPLogsV2)), + withPostRequest(func(w http.ResponseWriter, r *http.Request) error { + w.WriteHeader(http.StatusNoContent) + // Write "Ok" as the response body + _, _ = w.Write([]byte("Ok")) + return nil + }))...) +} + +//var OTLPLogsV2 = Build( +// append(WithExtraMiddlewareDefault, +// withTSAndSampleService, +// withSimpleParser("*", Parser(unmarshal.UnmarshalOTLPLogsV2)), +// withPostRequest(func(r *http.Request, w http.ResponseWriter) error { +// w.WriteHeader(http.StatusNoContent) +// // Write "Ok" as the response body +// _, _ = w.Write([]byte("Ok")) +// return nil +// }))...) diff --git a/writer/controller/middleware.go b/writer/controller/middleware.go new file mode 100644 index 00000000..a3a25a6b --- /dev/null +++ b/writer/controller/middleware.go @@ -0,0 +1,254 @@ +package controllerv1 + +import ( + "bytes" + "compress/gzip" + "context" + "encoding/json" + "fmt" + "github.com/golang/snappy" + "github.com/metrico/qryn/writer/ch_wrapper" + custom_errors "github.com/metrico/qryn/writer/utils/errors" + "io" + "net/http" + "strconv" + "strings" + "sync" + "time" +) + +var DbClient ch_wrapper.IChClient + +type cacheItem struct { + value uint64 // Assuming it stores an int64 value for logs data + timestamp time.Time // Timestamp when the item was cached + organizationID string +} + +var cacheData sync.Map + +type ResponseWriter struct { + header http.Header + status int + body []byte +} + +func WithPreRequest(preRequest Requester) BuildOption { + return func(ctx *PusherCtx) *PusherCtx { + ctx.PreRequest = append(ctx.PreRequest, preRequest) + return ctx + } +} + +func withPostRequest(postRequest Requester) BuildOption { + return func(ctx *PusherCtx) *PusherCtx { + ctx.PostRequest = append(ctx.PostRequest, postRequest) + return ctx + } +} + +func withSimpleParser(contentType string, parser Parser) BuildOption { + return func(ctx *PusherCtx) *PusherCtx { + ctx.Parser[contentType] = func(w http.ResponseWriter, r *http.Request) error { + // Assuming doParse function signature is compatible with Parser + return doParse(r, parser) + + } + return ctx + } +} + +func withComplexParser(contentType string, parser Parser, options ...BuildOption) BuildOption { + pusherCtx := &PusherCtx{ + Parser: make(map[string]Requester), + } + + // Apply options to pusherCtx + for _, o := range options { + pusherCtx = o(pusherCtx) + } + + // Define parser for contentType + pusherCtx.Parser["*"] = func(w http.ResponseWriter, r *http.Request) error { + return doParse(r, parser) + } + + // Return BuildOption function + return func(ctx *PusherCtx) *PusherCtx { + // Set the parser for contentType in ctx + ctx.Parser[contentType] = pusherCtx.Do + return ctx + } +} + +func withOkStatusAndBody(status int, body []byte) BuildOption { + return func(ctx *PusherCtx) *PusherCtx { + ctx.PostRequest = append(ctx.PostRequest, func(w http.ResponseWriter, r *http.Request) error { + w.WriteHeader(status) + w.Write(body) + return nil + }) + return ctx + } +} + +func withOkStatusAndJSONBody(status int, body map[string]interface{}) BuildOption { + return func(ctx *PusherCtx) *PusherCtx { + ctx.PostRequest = append(ctx.PostRequest, func(w http.ResponseWriter, r *http.Request) error { + // Marshal the JSON body + respBody, err := json.Marshal(body) + if err != nil { + return err + } + w.WriteHeader(status) + w.Write(respBody) + return nil + }) + return ctx + } +} + +func withParserContext(fn func(http.ResponseWriter, *http.Request, context.Context) (context.Context, error)) BuildOption { + return WithPreRequest(func(w http.ResponseWriter, r *http.Request) error { + ctx := r.Context() + parserCtx, err := fn(w, r, ctx) // Pass writer, request, and context to the parser function + if err != nil { + return err + } + // Update the request context with the parser context + *r = *r.WithContext(parserCtx) + return nil + }) +} + +var withUnsnappyRequest = WithPreRequest(func(w http.ResponseWriter, r *http.Request) error { + compressed, err := io.ReadAll(r.Body) + if err != nil { + return err + } + ctx := r.Context() + uncompressed, err := func() ([]byte, error) { + uncompressedLen, err := snappy.DecodedLen(compressed) + if err != nil { + return nil, err + } + if uncompressedLen > 10*1024*1024 { + return nil, custom_errors.New400Error("body is too long") + + } + uncompressed, err := snappy.Decode(nil, compressed) + if err != nil { + return nil, err + } + + return uncompressed, nil + }() + if err != nil { + ctx = context.WithValue(ctx, "bodyStream", bytes.NewBuffer(compressed)) + *r = *r.WithContext(ctx) + // Sending the compressed body back + } else { + // Reset the request body with the uncompressed data + ctx = context.WithValue(ctx, "bodyStream", bytes.NewBuffer(uncompressed)) + *r = *r.WithContext(ctx) + } + + return nil +}) + +type readColser struct { + io.Reader +} + +func (rc readColser) Close() error { return nil } + +var WithOverallContextMiddleware = WithPreRequest(func(w http.ResponseWriter, r *http.Request) error { + dsn := strings.Clone(r.Header.Get("X-CH-DSN")) + meta := strings.Clone(r.Header.Get("X-Scope-Meta")) + strTTLDays := strings.Clone(r.Header.Get("X-Ttl-Days")) + async := getAsyncMode(r) + TTLDays := uint16(0) + if strTTLDays != "" { + iTTLDays, err := strconv.ParseUint(strTTLDays, 10, 16) + if err == nil { + TTLDays = uint16(iTTLDays) + } + } + + switch r.Header.Get("Content-Encoding") { + case "": + // No encoding, do nothing + case "gzip": + reader, err := gzip.NewReader(r.Body) + if err != nil { + return err + } + r.Body = readColser{reader} + case "snappy": + reader := snappy.NewReader(r.Body) + r.Body = readColser{reader} + // Handle snappy encoding if needed + break + default: + return custom_errors.New400Error(fmt.Sprintf("%s encoding not supported", r.Header.Get("Content-Encoding"))) + } + ctx := r.Context() + // Modify context as needed + ctx = context.WithValue(ctx, "DSN", dsn) + //ctx = context.WithValue(ctx, "oid", oid) + ctx = context.WithValue(ctx, "META", meta) + ctx = context.WithValue(ctx, "TTL_DAYS", TTLDays) + ctx = context.WithValue(ctx, "async", async) + //ctx = context.WithValue(ctx, "shard", shard) + *r = *r.WithContext(ctx) + return nil +}) + +var withTSAndSampleService = WithPreRequest(func(w http.ResponseWriter, r *http.Request) error { + + ctx := r.Context() + dsn := ctx.Value("DSN") + //// Assuming Registry functions are available and compatible with net/http + svc, err := Registry.GetSamplesService(dsn.(string)) + if err != nil { + return err + } + ctx = context.WithValue(r.Context(), "splService", svc) + + svc, err = Registry.GetTimeSeriesService(dsn.(string)) + if err != nil { + return err + } + ctx = context.WithValue(ctx, "tsService", svc) + + svc, err = Registry.GetProfileInsertService(dsn.(string)) + if err != nil { + return err + } + ctx = context.WithValue(ctx, "profileService", svc) + + nodeName := svc.GetNodeName() + ctx = context.WithValue(ctx, "node", nodeName) + *r = *r.WithContext(ctx) + return nil +}) + +var withTracesService = WithPreRequest(func(w http.ResponseWriter, r *http.Request) error { + dsn := r.Context().Value("DSN") + svc, err := Registry.GetSpansSeriesService(dsn.(string)) + if err != nil { + return err + } + + ctx := context.WithValue(r.Context(), "spanAttrsService", svc) + + svc, err = Registry.GetSpansService(dsn.(string)) + if err != nil { + return err + } + + ctx = context.WithValue(ctx, "spansService", svc) + ctx = context.WithValue(ctx, "node", svc.GetNodeName()) + *r = *r.WithContext(ctx) + return nil +}) diff --git a/writer/controller/miscController.go b/writer/controller/miscController.go new file mode 100644 index 00000000..daf17daf --- /dev/null +++ b/writer/controller/miscController.go @@ -0,0 +1,9 @@ +package controllerv1 + +var Ready = Build(withOkStatusAndBody(200, []byte("ok"))) + +var Config = Build(withOkStatusAndBody(200, []byte("Not supported"))) + +var HealthInflux = Build(withOkStatusAndBody(200, nil)) + +var PromHealthStub = Build(withOkStatusAndBody(200, nil)) diff --git a/writer/controller/profileController.go b/writer/controller/profileController.go new file mode 100644 index 00000000..e76f43e8 --- /dev/null +++ b/writer/controller/profileController.go @@ -0,0 +1,39 @@ +package controllerv1 + +import ( + "context" + "errors" + "github.com/metrico/qryn/writer/utils/unmarshal" + "net/http" +) + +func PushProfileV2(cfg MiddlewareConfig) func(w http.ResponseWriter, r *http.Request) { + return Build( + append(cfg.ExtraMiddleware, + withTSAndSampleService, + withParserContext(func(w http.ResponseWriter, req *http.Request, parserCtx context.Context) (context.Context, error) { + fromValue := req.URL.Query().Get("from") + + if fromValue == "" { + return nil, errors.New("please provide from value") + } + + nameValue := req.URL.Query().Get("name") + + if nameValue == "" { + return nil, errors.New("please provide name value") + } + untilValue := req.URL.Query().Get("until") + + if untilValue == "" { + return nil, errors.New("please provide until value") + } + + _ctx := context.WithValue(parserCtx, "from", fromValue) + _ctx = context.WithValue(_ctx, "name", nameValue) + _ctx = context.WithValue(_ctx, "until", untilValue) + return _ctx, nil + }), + withSimpleParser("*", Parser(unmarshal.UnmarshalProfileProtoV2)), + withOkStatusAndBody(200, []byte("{}")))...) +} diff --git a/writer/controller/promController.go b/writer/controller/promController.go new file mode 100644 index 00000000..e83d4776 --- /dev/null +++ b/writer/controller/promController.go @@ -0,0 +1,53 @@ +package controllerv1 + +import ( + "github.com/metrico/qryn/writer/utils/unmarshal" + "net/http" +) + +// swagger:route GET /api/v1/prom/remote/write Data WriteData +// +// Returns data from server in array +// +// --- +// Consumes: +// - application/json +// +// Produces: +// - application/json +// +// Security: +// - JWT +// - ApiKeyAuth +// +// +// SecurityDefinitions: +// JWT: +// type: apiKey +// name: Authorization +// in: header +// ApiKeyAuth: +// type: apiKey +// in: header +// name: Auth-Token +/// +// Responses: +// 201: body:TableUserList +// 400: body:FailureResponse + +func WriteStreamV2(cfg MiddlewareConfig) func(w http.ResponseWriter, r *http.Request) { + + return Build( + append(cfg.ExtraMiddleware, + withTSAndSampleService, + withUnsnappyRequest, + withSimpleParser("*", Parser(unmarshal.UnmarshallMetricsWriteProtoV2)), + withOkStatusAndBody(204, nil))...) +} + +//var WriteStreamV2 = Build( +// append(WithExtraMiddlewareDefault, +// withTSAndSampleService, +// withUnsnappyRequest, +// withSimpleParser("*", Parser(unmarshal.UnmarshallMetricsWriteProtoV2)), +// withOkStatusAndBody(204, nil))...) diff --git a/writer/controller/shared.go b/writer/controller/shared.go new file mode 100644 index 00000000..0a7a2844 --- /dev/null +++ b/writer/controller/shared.go @@ -0,0 +1,53 @@ +package controllerv1 + +import ( + "github.com/gofiber/fiber/v2" + "github.com/metrico/qryn/writer/service" + "github.com/metrico/qryn/writer/utils/httpresponse" + "github.com/metrico/qryn/writer/utils/logger" + "github.com/metrico/qryn/writer/utils/stat" + "net/http" + "runtime/debug" + "strings" +) + +func watchErr(err error) bool { + if err == nil { + return true + } + strErr := err.Error() + if strings.HasPrefix(strErr, "json parse error") { + stat.AddSentMetrics("json_parse_errors", 1) + return true + } + if strings.Contains(strErr, "connection reset by peer") { + stat.AddSentMetrics("connection_reset_by_peer", 1) + return true + } + return false +} + +func tamePanic(ctx *fiber.Ctx) { + if err := recover(); err != nil { + logger.Error(err, " stack:", string(debug.Stack())) + httpresponse.CreateBadResponse(ctx, 500, "internal server error") + } +} + +func getAsyncMode(r *http.Request) int { + header := r.Header.Get("X-Async-Insert") + switch header { + case "0": + return service.INSERT_MODE_SYNC + case "1": + return service.INSERT_MODE_ASYNC + default: + return service.INSERT_MODE_DEFAULT + } +} + +func badRequestError(message string) error { + err := *fiber.ErrBadRequest + err.Message = message + return &err +} diff --git a/writer/controller/tempoController.go b/writer/controller/tempoController.go new file mode 100644 index 00000000..cc756b8d --- /dev/null +++ b/writer/controller/tempoController.go @@ -0,0 +1,84 @@ +package controllerv1 + +import ( + "bytes" + "context" + "github.com/metrico/qryn/writer/utils/unmarshal" + "go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp" + "io" + "net/http" +) + +type TempoController struct { +} + +func PushV2(cfg MiddlewareConfig) func(w http.ResponseWriter, r *http.Request) { + return Build( + append(cfg.ExtraMiddleware, + withTracesService, + withSimpleParser("ndjson", Parser(unmarshal.UnmarshalZipkinNDJSONV2)), + withSimpleParser("*", Parser(unmarshal.UnmarshalZipkinJSONV2)), + withOkStatusAndBody(202, nil))...) +} + +var ClickhousePushV2 = PushV2 + +//var PushV2 = Build( +// append(WithExtraMiddlewareTempo, +// withTracesService, +// withSimpleParser("ndjson", Parser(unmarshal.UnmarshalZipkinNDJSONV2)), +// withSimpleParser("*", Parser(unmarshal.UnmarshalZipkinJSONV2)), +// withOkStatusAndBody(202, nil))...) + +//var ClickhousePushV2 = PushV2 + +func OTLPPushV2(cfg MiddlewareConfig) func(w http.ResponseWriter, r *http.Request) { + return Build( + append(cfg.ExtraMiddleware, + withTracesService, + WithPreRequest(func(w http.ResponseWriter, r *http.Request) error { + // Read the request body + body, err := io.ReadAll(r.Body) + if err != nil { + return err + } + defer r.Body.Close() + + // Create a new request context with the modified body + ctx := context.WithValue(r.Context(), "bodyStream", bytes.NewReader(body)) + *r = *r.WithContext(ctx) + return nil + + }), + withSimpleParser("*", Parser(unmarshal.UnmarshalOTLPV2)), + withOkStatusAndBody(200, func() []byte { + res, _ := ptraceotlp.NewResponse().MarshalProto() + return res + }()), + )...) + +} + +//var OTLPPushV2 = Build( +// append(WithExtraMiddlewareTempo, +// withTracesService, +// withPreRequest(func(r *http.Request, w http.ResponseWriter) error { +// // Read the request body +// body, err := io.ReadAll(r.Body) +// if err != nil { +// return err +// } +// defer r.Body.Close() +// +// // Create a new request context with the modified body +// ctx := context.WithValue(r.Context(), "bodyStream", bytes.NewReader(body)) +// *r = *r.WithContext(ctx) +// return nil +// +// }), +// withSimpleParser("*", Parser(unmarshal.UnmarshalOTLPV2)), +// withOkStatusAndBody(200, func() []byte { +// res, _ := ptraceotlp.NewResponse().MarshalProto() +// return res +// }()), +// )...) diff --git a/writer/e2e_test.go b/writer/e2e_test.go new file mode 100644 index 00000000..6a9ba61f --- /dev/null +++ b/writer/e2e_test.go @@ -0,0 +1,987 @@ +package writer + +import ( + "bytes" + "context" + "flag" + "fmt" + "github.com/gorilla/mux" + clconfig "github.com/metrico/cloki-config" + "github.com/metrico/qryn/reader/utils/middleware" + "github.com/metrico/qryn/writer/ch_wrapper" + "github.com/metrico/qryn/writer/plugin" + "github.com/metrico/qryn/writer/utils/logger" + "github.com/mochi-co/mqtt/server/listeners" + "github.com/openzipkin/zipkin-go/model" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" + "io" + "math" + "math/rand" + "net" + "net/http" + "os" + "sort" + "strconv" + "strings" + "testing" + "time" + + "github.com/golang/snappy" + json "github.com/json-iterator/go" + "github.com/m3db/prometheus_remote_client_golang/promremote" + "github.com/metrico/qryn/writer/config" + "github.com/metrico/qryn/writer/utils/proto/logproto" + mqtt "github.com/mochi-co/mqtt/server" + zipkin "github.com/openzipkin/zipkin-go/reporter/http" + "github.com/stretchr/testify/assert" + "github.com/valyala/fasthttp" + "google.golang.org/protobuf/proto" +) + +/* +stream / values format + +{ + "streams": [ + { + "stream": { + "label": "value" + }, + "values": [ + [ "<unix epoch in nanoseconds>", "<log line>" ], + [ "<unix epoch in nanoseconds>", "<log line>" ] + ] + } + ] +} + +*/ + +var serviceInfo plugin.ServicesObject + +func genLines(_labels map[string]string, freqS float64, testid string, fromNS int64, toNS int64, + lineGen func(int) string, valGen func(int) float64) map[string]interface{} { + if lineGen == nil { + lineGen = func(i int) string { + return fmt.Sprintf("TEST_LINE_%d", i) + } + } + _labels["test_id"] = testid + freqNS := int64(float64(time.Second.Nanoseconds()) * freqS) + valLen := int64((toNS - fromNS) / freqNS) + values := make([][]interface{}, valLen) + for i := int64(0); i < valLen; i++ { + values[i] = []interface{}{ + fmt.Sprintf("%d", fromNS+(i*freqNS)), + lineGen(int(i)), + } + if valGen != nil { + values[i] = append(values[i], valGen(int(i))) + } + } + fmt.Printf("Created %d VALUES\n", len(values)) + return map[string]interface{}{ + "stream": _labels, + "values": values, + } +} + +/* +labels / entries format + + + "streams": [ + { + "labels": "<LogQL label key-value pairs>", + "entries": [ + { + "ts": "<RFC3339Nano timestamp>", + "line": "<log line>" + } + ] + } + ] +} + +*/ + +func genOldLines(_labels map[string]string, freqS float64, testid string, fromNS int64, toNS int64, + lineGen func(int) string, valGen func(int) float64) map[string]interface{} { + if lineGen == nil { + lineGen = func(i int) string { + return fmt.Sprintf("TEST_LINE_%d", i) + } + } + strLabels := make([]string, len(_labels), len(_labels)+2) + i := int64(0) + for k, v := range _labels { + strLabels[i] = k + "=\"" + v + "\"" + } + strLabels = append(strLabels, fmt.Sprintf("test_id=\"%s\"", testid)) + freqNS := int64(float64(time.Second.Nanoseconds()) * freqS) + entLen := int64((toNS - fromNS) / freqNS) + entries := make([]map[string]interface{}, entLen) + size := 0 + for i = 0; i < entLen; i++ { + entries[i] = map[string]interface{}{ + "ts": fmt.Sprintf("%d", fromNS+(i*freqNS)), + "line": lineGen(int(i)), + } + if valGen != nil { + entries[i]["value"] = valGen(int(i)) + } + size += len(entries[i]["ts"].(string)) + len(entries[i]["line"].(string)) + } + fmt.Printf("Created %d VALUES\n", i) + return map[string]interface{}{ + "labels": "{" + strings.Join(strLabels, ",") + "}", + "entries": entries, + } +} + +func genProtoLines(_labels map[string]string, freqS float64, testid string, fromNS int64, toNS int64, + lineGen func(int) string) []byte { + if lineGen == nil { + lineGen = func(i int) string { + return fmt.Sprintf("TEST_LINE_%d", i) + } + } + _labels["test_id"] = testid + strLabels := make([]string, len(_labels)) + i := 0 + for k, v := range _labels { + strLabels[i] = fmt.Sprintf(`%s="%s"`, k, v) + i++ + } + req := logproto.PushRequest{Streams: make([]*logproto.StreamAdapter, 1)} + req.Streams[0] = &logproto.StreamAdapter{ + Labels: "{" + strings.Join(strLabels, ",") + "}", + Entries: make([]*logproto.EntryAdapter, 0, 1000), + } + for i := fromNS; i < toNS; i += int64(freqS * 1e9) { + req.Streams[0].Entries = append(req.Streams[0].Entries, &logproto.EntryAdapter{ + Line: lineGen(int(i)), + Timestamp: &logproto.Timestamp{ + Seconds: i / 1e9, + Nanos: int32(i % 1e9), + }, + }) + } + fmt.Printf("Created %d VALUES\n", len(req.Streams[0].Entries)) + byteReq, err := proto.Marshal(&req) + if err != nil { + panic(err) + } + var compReq []byte = nil + compReq = snappy.Encode(compReq, byteReq) + return compReq +} + +func request(body []byte, contentType string) error { + fmt.Println("Requesting") + req := fasthttp.AcquireRequest() + req.SetBody(body) + req.Header.Set("Content-Type", contentType /*"application/json"*/) + req.Header.Set("X-Scope-OrgID", "1") + req.Header.Set("X-Logs-Daily-MB", "1000") + req.SetRequestURI("http://localhost:3215/loki/api/v1/push") + req.Header.SetMethod("POST") + resp := fasthttp.AcquireResponse() + err := fasthttp.Do(req, resp) + defer fasthttp.ReleaseResponse(resp) + defer fasthttp.ReleaseRequest(req) + if err != nil { + fmt.Println("Requesting ERR") + return err + } + if resp.StatusCode() != 204 { + fmt.Println("Requesting ERR #2") + return fmt.Errorf("[%d] %s", resp.StatusCode(), resp.Body()) + } + fmt.Println("Requesting OK") + return nil +} + +func getSamplesTable() string { + return config.Cloki.Setting.DATABASE_DATA[0].TableSamples +} + +func getTSTable() string { + return config.Cloki.Setting.DATABASE_DATA[0].TableSeries +} + +func getTestIDData(testID string) []string { + var fp uint64 + //client, err := adapter.NewClient(context.Background(), &config.Cloki.Setting.DATABASE_DATA[0], true) + client, err := ch_wrapper.NewSmartDatabaseAdapter(&config.Cloki.Setting.DATABASE_DATA[0], true) + if err != nil { + panic(err) + } + err = client.GetFirst(fmt.Sprintf("select distinct fingerprint from "+getTSTable()+" where "+ + "JSONExtractString(labels, 'test_id') == '%s' AND org_id == '1'", testID), &fp) + logger.Info("select distinct fingerprint from "+getTSTable()+" where "+ + "JSONExtractString(labels, 'test_id') == '%s' AND org_id == '1'", testID) + if err != nil { + fmt.Println("Error 1...", err.Error()) + panic(err) + } + + arr, err := client.GetList(fmt.Sprintf("select formatRow('TSV', timestamp_ns, "+ + "arraySort(JSONExtractKeysAndValues(labels, 'String')), string, value) "+ + "from "+getSamplesTable()+" as samples_v4 "+ + "left any join "+config.Cloki.Setting.DATABASE_DATA[0].Name+".time_series_v2 "+ + " ON samples_v4.fingerprint == time_series_v2.fingerprint "+ + " where fingerprint = %d AND org_id == '1' ORDER BY timestamp_ns ASC", fp)) + if err != nil { + fmt.Println("Error 2..... ", err.Error()) + panic(err) + } + return arr +} + +func testPrometheusPush(t *testing.T, testid int64) { + cfg := promremote.NewConfig( + promremote.WriteURLOption("http://localhost:3215/prom/remote/write"), + promremote.HTTPClientTimeoutOption(60*time.Second), + promremote.UserAgent("go-test"), + ) + + client, err := promremote.NewClient(cfg) + if err != nil { + t.Fatal(fmt.Errorf("unable to construct client: %v", err)) + } + now := time.Now() + timeSeriesList := []promremote.TimeSeries{ + { + Labels: []promremote.Label{ + { + Name: "test_id", + Value: fmt.Sprintf("foo_bar_%d", testid), + }, + { + Name: "biz", + Value: "baz", + }, + }, + Datapoint: promremote.Datapoint{ + Timestamp: now, + Value: 1415.92, + }, + }, + } + if _, err = client.WriteTimeSeries(context.Background(), timeSeriesList, promremote.WriteOptions{ + Headers: map[string]string{"X-Scope-OrgID": "1"}, + }); err != nil { + t.Fatal(err) + } + time.Sleep(time.Second * 2) + values := getTestIDData(fmt.Sprintf("foo_bar_%d", testid)) + assert.Equal(t, []string{fmt.Sprintf( + "%d\t[('biz','baz'),('test_id','foo_bar_%d')]\t\t1415.92\n", + now.UnixMilli()*1000000, + testid, + )}, values) +} + +func mustMarshal(i interface{}) []byte { + res, err := json.Marshal(i) + if err != nil { + panic(err) + } + return res +} + +var appFlags CommandLineFlags + +// params for Flags +type CommandLineFlags struct { + InitializeDB *bool `json:"initialize_db"` + ShowHelpMessage *bool `json:"help"` + ShowVersion *bool `json:"version"` + ConfigPath *string `json:"config_path"` +} + +/* init flags */ +func initFlags() { + appFlags.InitializeDB = flag.Bool("initialize_db", false, "initialize the database and create all tables") + appFlags.ShowHelpMessage = flag.Bool("help", false, "show help") + appFlags.ShowVersion = flag.Bool("version", false, "show version") + appFlags.ConfigPath = flag.String("config", "", "the path to the config file") + flag.Parse() +} + +func TestE2E(t *testing.T) { + if os.Getenv("E2E") != "1" { + return + } + os.Args = append(os.Args, "-config", os.Getenv("CONFIG")) + rand.Seed(time.Now().UnixNano()) + testId := rand.Int63() + go runPrometheus(testId) + go runMQTT() + + initFlags() + + /* first check admin flags */ + // checkHelpVersionFlags() + var configPaths []string + if _, err := os.Stat(*appFlags.ConfigPath); err == nil { + configPaths = append(configPaths, *appFlags.ConfigPath) + } + config.Cloki = clconfig.New(clconfig.CLOKI_WRITER, configPaths, "", "") + + ////ReadConfig + config.Cloki.ReadConfig() + //checkLicenseFlags() + + app := mux.NewRouter() + if config.Cloki.Setting.AUTH_SETTINGS.BASIC.Username != "" && + config.Cloki.Setting.AUTH_SETTINGS.BASIC.Password != "" { + app.Use(middleware.BasicAuthMiddleware(config.Cloki.Setting.AUTH_SETTINGS.BASIC.Username, + config.Cloki.Setting.AUTH_SETTINGS.BASIC.Password)) + } + app.Use(middleware.AcceptEncodingMiddleware) + if config.Cloki.Setting.HTTP_SETTINGS.Cors.Enable { + app.Use(middleware.CorsMiddleware(config.Cloki.Setting.HTTP_SETTINGS.Cors.Origin)) + } + app.Use(middleware.LoggingMiddleware("[{{.status}}] {{.method}} {{.url}} - LAT:{{.latency}}")) + + Init(config.Cloki, app) + var pluginInfo = &plugin.QrynWriterPlugin{} + //pluginInfo.Initialize(appFlags) + //////License After Config - need check some params + // + //pluginInfo.RegisterRoutes(*config.Cloki.Setting) + + serviceInfo = pluginInfo.ServicesObject + //go main() + time.Sleep(5 * time.Second) + + end := time.Now().UnixNano() / 1e6 * 1e6 + start := end - time.Second.Nanoseconds()*2 + mid := start + time.Second.Nanoseconds() + ln := func(i int) string { + return "LINE" + } + val := func(i int) float64 { + return 123 + } + lines1 := map[string]interface{}{ + "streams": []map[string]interface{}{ + genLines(map[string]string{"test1": "val1"}, 1, fmt.Sprintf("TEST_%d", testId), + start, mid, ln, val), + }, + } + lines2 := map[string]interface{}{ + "streams": []map[string]interface{}{ + genLines(map[string]string{"test1": "val1"}, 1, fmt.Sprintf("TEST_%d", testId), + mid, end, ln, val), + }, + } + err := request(mustMarshal(lines1), "application/json") + if err != nil { + t.Fatal(err) + } + err = request(mustMarshal(lines2), "application/json") + if err != nil { + t.Fatal(err) + } + + oldLines := map[string]interface{}{ + "streams": []map[string]interface{}{ + genOldLines(map[string]string{"test1": "val1"}, 1, fmt.Sprintf("OLD_TEST_%d", testId), + start, end, ln, val), + }, + } + err = request(mustMarshal(oldLines), "application/json") + if err != nil { + t.Fatal(err) + } + err = request( + genProtoLines( + map[string]string{"test1": "val1"}, 1, + fmt.Sprintf("PROTO_TEST_%d", testId), + start, end, ln), + "application/x-protobuf") + if err != nil { + t.Fatal(err) + } + time.Sleep(2 * time.Second) + fmt.Printf("TEST ID: %d\n", testId) + getTestRes := func(id string, val float64) []string { + return []string{ + fmt.Sprintf("%d [('test1','val1'),('test_id','%s')] LINE %d\n", start, id, int(val)), + fmt.Sprintf("%d [('test1','val1'),('test_id','%s')] LINE %d\n", mid, id, int(val)), + } + } + time.Sleep(2 * time.Second) + values := getTestIDData(fmt.Sprintf("TEST_%d", testId)) + assert.Equal(t, getTestRes(fmt.Sprintf("TEST_%d", testId), 123), values) + values = getTestIDData(fmt.Sprintf("OLD_TEST_%d", testId)) + assert.Equal(t, getTestRes(fmt.Sprintf("OLD_TEST_%d", testId), 123), values) + values = getTestIDData(fmt.Sprintf("PROTO_TEST_%d", testId)) + assert.Equal(t, getTestRes(fmt.Sprintf("PROTO_TEST_%d", testId), 0), values) + testPrometheusPush(t, testId) + testPrometheusScrape(t, testId) + testTempoZipkinIngest(t, uint64(testId)) + miscTest() + ingestInfluxTest(t, uint64(testId)) + time.Sleep(5 * time.Second) + testUsageCounting(t) + //ingestInfluxJSONTest(t, uint64(testId)) +} + +var mqttServer *mqtt.Server + +func runMQTT() { + mqttServer = mqtt.New() + tcp := listeners.NewTCP("t1", ":1883") + err := mqttServer.AddListener(tcp, nil) + if err != nil { + panic(err) + } + err = mqttServer.Serve() + if err != nil { + panic(err) + } +} + +func testMQTT(t *testing.T) { + now := time.Now().UnixNano() + mqttID := fmt.Sprintf("MQTT_%d", rand.Uint64()) + msg := fmt.Sprintf(`{"ts":%d, "test_id": "%s"}`, now, mqttID) + err := mqttServer.Publish("test/test1", []byte(msg), false) + if err != nil { + panic(err) + } + time.Sleep(time.Second) + values := getTestIDData(mqttID) + assert.Equal(t, []string{ + fmt.Sprintf( + "%d\t[('f1','v1'),('test_id','%s'),('topic','test/test1')]\t%s\t0\n", + now, mqttID, msg), + }, values) +} + +var promTestGauge prometheus.Gauge = nil +var promTestCounter prometheus.Counter = nil +var promTestHist prometheus.Histogram = nil +var promTestSumm prometheus.Summary = nil +var metricsSrv *http.Server + +func runPrometheus(testID int64) error { + cLbls := map[string]string{ + + "test": fmt.Sprintf("promtest_%d", testID), + } + promTestGauge = promauto.NewGauge(prometheus.GaugeOpts{ + Namespace: "test", + Subsystem: "test", + Name: fmt.Sprintf("testG_%d", testID), + Help: "test gauge", + ConstLabels: cLbls, + }) + promTestCounter = promauto.NewCounter(prometheus.CounterOpts{ + Namespace: "test", + Subsystem: "test", + Name: fmt.Sprintf("testCnt_%d", testID), + Help: "test counter", + ConstLabels: cLbls, + }) + promTestHist = promauto.NewHistogram(prometheus.HistogramOpts{ + Namespace: "test", + Subsystem: "test", + Name: fmt.Sprintf("testHist_%d", testID), + Help: "test hist", + ConstLabels: cLbls, + Buckets: []float64{0, 10, 20, 30, 40, 50}, + }) + promTestSumm = promauto.NewSummary(prometheus.SummaryOpts{ + Namespace: "test", + Subsystem: "test", + Name: fmt.Sprintf("testSumm_%d", testID), + Help: "test summ", + ConstLabels: cLbls, + Objectives: nil, + MaxAge: time.Minute, + AgeBuckets: 5, + BufCap: 1000, + }) + sm := http.NewServeMux() + sm.Handle("/metrics", promhttp.Handler()) + metricsSrv = &http.Server{Addr: ":2112", Handler: sm} + go metricsSrv.ListenAndServe() + return nil +} + +func testPrometheusScrape(t *testing.T, testID int64) { + promTestGauge.Set(10) + promTestCounter.Add(1) + for i := 0; i <= 50; i = i + 10 { + promTestHist.Observe(float64(i)) + promTestSumm.Observe(float64(i)) + promTestHist.Observe(float64(i)) + promTestSumm.Observe(float64(i)) + } + time.Sleep(time.Second * 6) + metricsSrv.Close() + + bytes := make([]byte, 0, 10000) + code, bytes, _ := fasthttp.Get(bytes, "http://localhost:2112/metrics") + fmt.Printf("[%d]: %s\n", code, bytes) + //client, err := adapter.NewClient(context.Background(), &config.Cloki.Setting.DATABASE_DATA[0], true) + client, err := ch_wrapper.NewSmartDatabaseAdapter(&config.Cloki.Setting.DATABASE_DATA[0], true) + labels, err := client.GetList(fmt.Sprintf("SELECT DISTINCT labels "+ + "FROM "+getTSTable()+" WHERE JSONExtractString(labels, 'test') == 'promtest_%d' and org_id=='1' "+ + "ORDER BY labels", testID)) + + if err != nil { + t.Fatal(err) + } + + labelsSet := make([]string, 0, 10) + for _, label := range labels { + label = strings.Replace(label, fmt.Sprintf("%d", testID), "", -1) + labelsSet = append(labelsSet, label) + } + labelsMap := make([]map[string]string, 0, 20) + for _, l := range labelsSet { + _map := map[string]string{} + json.UnmarshalFromString(l, &_map) + labelsMap = append(labelsMap, _map) + } + sort.Slice(labelsMap, func(i, j int) bool { + return fmt.Sprintf("%v", labelsMap[i]) < fmt.Sprintf("%v", labelsMap[j]) + }) + for _, l := range labelsMap { + fmt.Printf("%v\n", l) + } + assert.Equal(t, []map[string]string{ + {"_SUBTYPE_": "bucket", "__name__": "test_test_testHist_", "__type__": "HISTOGRAM", "endpoint": "test_end", "instance": "test", "le": "+Inf", "test": "promtest_"}, + {"_SUBTYPE_": "bucket", "__name__": "test_test_testHist_", "__type__": "HISTOGRAM", "endpoint": "test_end", "instance": "test", "le": "0", "test": "promtest_"}, + {"_SUBTYPE_": "bucket", "__name__": "test_test_testHist_", "__type__": "HISTOGRAM", "endpoint": "test_end", "instance": "test", "le": "10", "test": "promtest_"}, + {"_SUBTYPE_": "bucket", "__name__": "test_test_testHist_", "__type__": "HISTOGRAM", "endpoint": "test_end", "instance": "test", "le": "20", "test": "promtest_"}, + {"_SUBTYPE_": "bucket", "__name__": "test_test_testHist_", "__type__": "HISTOGRAM", "endpoint": "test_end", "instance": "test", "le": "30", "test": "promtest_"}, + {"_SUBTYPE_": "bucket", "__name__": "test_test_testHist_", "__type__": "HISTOGRAM", "endpoint": "test_end", "instance": "test", "le": "40", "test": "promtest_"}, + {"_SUBTYPE_": "bucket", "__name__": "test_test_testHist_", "__type__": "HISTOGRAM", "endpoint": "test_end", "instance": "test", "le": "50", "test": "promtest_"}, + {"_SUBTYPE_": "count", "__name__": "test_test_testHist_", "__type__": "HISTOGRAM", "endpoint": "test_end", "instance": "test", "test": "promtest_"}, + {"_SUBTYPE_": "count", "__name__": "test_test_testSumm_", "__type__": "SUMMARY", "endpoint": "test_end", "instance": "test", "test": "promtest_"}, + {"_SUBTYPE_": "sum", "__name__": "test_test_testHist_", "__type__": "HISTOGRAM", "endpoint": "test_end", "instance": "test", "test": "promtest_"}, + {"_SUBTYPE_": "sum", "__name__": "test_test_testSumm_", "__type__": "SUMMARY", "endpoint": "test_end", "instance": "test", "test": "promtest_"}, + {"__name__": "test_test_testCnt_", "__type__": "COUNTER", "endpoint": "test_end", "instance": "test", "test": "promtest_"}, + {"__name__": "test_test_testG_", "__type__": "GAUGE", "endpoint": "test_end", "instance": "test", "test": "promtest_"}, + }, labelsMap) + var count uint64 = 0 + var sum float64 = 0 + var max float64 = 0 + var min float64 = 0 + + err = client.Scan(context.Background(), fmt.Sprintf("SELECT count(1), max(value), min(value), sum(value) FROM "+getSamplesTable()+ + " WHERE fingerprint IN (SELECT fingerprint "+ + "FROM "+serviceInfo.DatabaseNodeMap[0].Name+".time_series_v2 "+ + "WHERE JSONExtractString(labels, 'test') == 'promtest_%d') AND "+ + " timestamp_ns > toUnixTimestamp(NOW() - INTERVAL '10 minute') * 1000000000 AND org_id=='1' AND value != 0", testID), + nil, &count, &max, &min, &sum) + if err != nil { + t.Fatal(err) + } + + var expectedSum float64 = 2756 + if count >= 65 { + expectedSum = 3445 + } + if count >= 78 { + expectedSum = 4134 + } + fmt.Printf("%d %f %f %f\n", count, min, max, sum) + assert.Equal(t, 1., min) + assert.Equal(t, 300., max) + assert.True(t, math.Abs(expectedSum-sum) < 100) +} + +type zipkinDoer struct { + onSend chan error +} + +func (z *zipkinDoer) Do(req *http.Request) (*http.Response, error) { + req.Header.Set("X-Scope-OrgID", "1") + client := &http.Client{} + res, err := client.Do(req) + z.onSend <- err + return res, err +} + +func testTempoZipkinIngest(t *testing.T, testid uint64) { + err := ingestTestZipkinSpan(testid, "http://localhost:3215/tempo/spans") + if err != nil { + logger.Info("testTempoZipkinIngest Error", err.Error()) + t.Fatal(err) + } + fmt.Println(checkZipkinSpan(testid)) + fmt.Println("Send /api/v2/spans") + err = ingestTestZipkinSpan(testid+1, "http://localhost:3215/api/v2/spans") + if err != nil { + t.Fatal(err) + } + fmt.Println(checkZipkinSpan(testid + 1)) + fmt.Println("Send /api/v2/spans OK") + fmt.Println("Sending 10MB spans") + trace := `{"traceId":"%016x0000000000000000","name":"request_received","id":"%016x","timestamp":%d,"duration":343379,"localEndpoint":{"serviceName":"dummy-server"},"tags":{"job":"dummy-server","entity":"Shmi Skywalker_olive","http.status_code":"200","otel.status_code":"OK","service.name":"dummy-server","telemetry.sdk.language":"nodejs","telemetry.sdk.name":"opentelemetry","telemetry.sdk.version":"1.5.0"}}` + traces := []string{} + length := 0 + j := testid + 2 + for length < 10*1024*1024 { + _trace := fmt.Sprintf(trace, j, j, time.Now().UnixMicro()) + traces = append(traces, _trace) + j++ + length += len(_trace) + } + req, err := http.NewRequest("POST", "http://localhost:3215/tempo/spans", bytes.NewReader([]byte( + "["+strings.Join(traces, ",")+"]"))) + if err != nil { + panic(err) + } + req.Header.Set("X-Scope-OrgID", "1") + req.ContentLength = 0 + client := http.Client{} + resp, err := client.Do(req) + fmt.Println("Sending 10MB spans Done") + if err != nil { + panic(err) + } + if resp.StatusCode/100 != 2 { + bResp, _ := io.ReadAll(resp.Body) + panic(fmt.Sprintf("[%d]: %s", resp.StatusCode, string(bResp))) + } + for k := testid + 2; k < j; k += 1000 { + testIDs := []uint64{} + for l := k; l < j && l < k+1000; l++ { + testIDs = append(testIDs, l) + } + payloads := checkZipkinSpan(testIDs...) + for l, p := range payloads { + if p != traces[k+uint64(l)-testid-2] { + panic(fmt.Sprintf("trace %s != %s", p, traces[k+uint64(l)-testid-2])) + } + } + + } + fmt.Println("Sending 10MB spans Ok") +} + +func ingestTestZipkinSpan(traceId uint64, url string) error { + start := time.Now() + onSend := make(chan error) + reporter := zipkin.NewReporter(url, zipkin.Client(&zipkinDoer{onSend})) + defer func() { + close(onSend) + }() + defer reporter.Close() + reporter.Send(model.SpanModel{ + SpanContext: model.SpanContext{ + TraceID: model.TraceID{ + High: traceId, + Low: 0, + }, + ID: model.ID(traceId), + }, + Name: "testspan1", + Timestamp: start, + Duration: 1000, + Shared: false, + LocalEndpoint: &model.Endpoint{ + ServiceName: "service1", + IPv4: net.IPv4(192, 168, 0, 1), + IPv6: nil, + Port: 8080, + }, + Annotations: []model.Annotation{ + {start, "annotation1"}, + }, + Tags: map[string]string{ + "test_id": strconv.FormatUint(traceId, 10), + }, + }) + return <-onSend +} + +func checkZipkinSpan(traceIDs ...uint64) []string { + strTraceIDs := make([]string, len(traceIDs)) + strSpanIDs := make([]string, len(traceIDs)) + for i, traceID := range traceIDs { + strTraceIDs[i] = fmt.Sprintf("%016x0000000000000000", traceID) + strSpanIDs[i] = fmt.Sprintf("%016x", traceID) + } + //client, err := adapter.NewClient(context.Background(), &config.Cloki.Setting.DATABASE_DATA[0], true) + client, err := ch_wrapper.NewSmartDatabaseAdapter(&config.Cloki.Setting.DATABASE_DATA[0], true) + if err != nil { + panic(err) + } + q := fmt.Sprintf( + "SELECT payload FROM test.tempo_traces "+ + "WHERE lowerUTF8(hex(trace_id)) IN (%s) AND lowerUTF8(hex(span_id)) IN (%s) and oid = '1' "+ + "ORDER BY timestamp_ns ASC, trace_id asc", + fmt.Sprintf("'%s'", strings.Join(strTraceIDs[:], "','")), + fmt.Sprintf("'%s'", strings.Join(strSpanIDs[:], "','")), + ) + res, err := client.GetList(q) + if len(res) != len(traceIDs) { + panic(fmt.Sprintf("COUNT mismatch: %d != %d", len(res), len(traceIDs))) + } + return res +} + +func miscTest() { + for _, url := range []string{"http://localhost:3215/metrics", "http://localhost:3215/config", + "http://localhost:3215/ready"} { + resp, err := http.Get(url) + if err != nil { + panic(err) + } + if resp.StatusCode/100 != 2 { + body, _ := io.ReadAll(resp.Body) + panic(fmt.Sprintf("miscTest: [%d]: %s", resp.StatusCode, string(body))) + } + } + +} + +func ingestInfluxTest(t *testing.T, testId uint64) { + fmt.Println("GENERATING 5 mb influx") + testLine := `logs,tag1=val1,test_id=%d,format=influx,type=logs message="%s",value=%d %d` + lines := []string{} + length := 0 + logsCnt := 0 + startTS := time.Now().UnixNano() + logsSize := 0 + for length < 5*1024*1024 { + msg := fmt.Sprintf("this is a very very long test string #%d", logsCnt) + line := fmt.Sprintf(testLine, testId, msg, logsCnt, time.Now().UnixNano()) + lines = append(lines, line) + length += len(line) + logsCnt++ + logsSize += len(msg) + } + testMetricsLine := `logs,tag1=val1,test_id=%d,format=influx,type=metrics metric1=%d,metric2=%d %d` + metricsCnt := 0 + for length < 10*1024*1024 { + line := fmt.Sprintf(testMetricsLine, testId, metricsCnt, metricsCnt+1, time.Now().UnixNano()) + lines = append(lines, line) + length += len(line) + metricsCnt++ + } + endTS := time.Now().UnixNano() + fmt.Printf("SENDING 10 mb influx (%d logs)\n", logsSize) + req, err := http.NewRequest("POST", "http://localhost:3215/influx/api/v2/write", + bytes.NewReader([]byte(strings.Join(lines, "\r\n")))) + if err != nil { + panic(err) + } + req.Header.Set("X-Scope-OrgID", "1") + req.ContentLength = 0 + client := http.Client{} + resp, err := client.Do(req) + if err != nil { + panic(err) + } + if resp.StatusCode/100 != 2 { + panic(fmt.Sprintf("[%d]: %s", resp.StatusCode, string(readAllNoErr(resp.Body)))) + } + fmt.Println("CHECKING 10 mb influx") + + //CHClient, err := adapter.NewClient(context.Background(), &config.Cloki.Setting.DATABASE_DATA[0], true) + CHClient, err := ch_wrapper.NewSmartDatabaseAdapter(&config.Cloki.Setting.DATABASE_DATA[0], true) + if err != nil { + panic(err) + } + var fp uint64 + err = CHClient.GetFirst(fmt.Sprintf(`SELECT fingerprint FROM time_series_gin_v2 WHERE + (key, val) IN (('format', 'influx') as c1, ('test_id', '%s') as c2, ('type', 'logs') as c3) AND org_id = '1' GROUP BY fingerprint HAVING + sum(((key, val) == c1) + ((key, val) == c2) * 2 + ((key, val) == c3) * 4) == 7`, strconv.FormatUint(testId, 10)), + &fp) + if err != nil { + panic(err) + } + + rows, err := CHClient.GetList(fmt.Sprintf(`SELECT formatRow('TSV', string, value) FROM samples_v4 +WHERE fingerprint = %d AND org_id = '1' AND timestamp_ns >= %d AND timestamp_ns <= %d ORDER BY timestamp_ns ASC`, + fp, startTS, endTS)) + + j := 0 + for _, row := range rows { + row = strings.Trim(row, " \t\r\n") + expected := fmt.Sprintf("message=\"this is a very very long test string #%d\" value=%d\t0", j, j) + if row != expected { + panic(fmt.Sprintf("influx error: `%s` != `%s`", row, expected)) + } + j++ + } + if j != logsCnt { + t.Fatalf("inclux error: ingested strings number %d != %d", j, logsCnt) + } + + for add, metricName := range []string{"metric1", "metric2"} { + err := CHClient.GetFirst(fmt.Sprintf(`SELECT fingerprint FROM time_series_gin_v2 WHERE + (key, val) IN (('format', 'influx') as c1, ('test_id', '%s') as c2, ('type', 'metrics') as c3, ('__name__', '%s') as c4) AND org_id = '1' GROUP BY fingerprint HAVING + sum(((key, val) == c1) + ((key, val) == c2) * 2 + ((key, val) == c3) * 4 + ((key, val) == c4) * 8) == 15`, + strconv.FormatUint(testId, 10), metricName), &fp) + if err != nil { + panic(err) + } + + rows, err = CHClient.GetList(fmt.Sprintf(`SELECT formatRow('TSV', string, value) FROM samples_v4 + WHERE fingerprint = %d AND org_id = '1' AND timestamp_ns >= %d AND timestamp_ns <= %d ORDER BY timestamp_ns ASC`, + fp, startTS, endTS)) + j = 0 + for _, row := range rows { + row = strings.Trim(row, " \t\r\n") + expected := fmt.Sprintf("%d", j+add) + if row != expected { + panic(fmt.Sprintf("influx error: `%s` != `%s`", row, expected)) + } + j++ + } + if j != metricsCnt { + t.Fatalf("inclux error: ingested strings number %d != %d", j, logsCnt) + } + } + fmt.Println("SENDING 10 mb influx OK") +} + +func ingestInfluxJSONTest(t *testing.T, testId uint64) { + fmt.Println("GENERATING 10 mb influx json") + testLine := `{"timestamp_ns":"%d", "tags":{"tag1":"val1","test_id":"%d","format":"influxjson","type":"logs"}, "fields":{"message":"this is a very very long test string #%d","value":"%d"}}` + lines := []string{} + length := 0 + logsCnt := 0 + startTS := time.Now().UnixNano() + for length < 10*1024*1024 { + line := fmt.Sprintf(testLine, time.Now().UnixNano(), testId, logsCnt, logsCnt) + lines = append(lines, line) + length += len(line) + logsCnt++ + } + endTS := time.Now().UnixNano() + fmt.Println("SENDING 10 mb influx json") + req, err := http.NewRequest("POST", "http://localhost:3215/influx/api/v2/write?type=ndjson", + bytes.NewReader([]byte(strings.Join(lines, "\r\n")))) + if err != nil { + panic(err) + } + req.Header.Set("X-Scope-OrgID", "1") + req.ContentLength = 0 + client := http.Client{} + resp, err := client.Do(req) + if err != nil { + panic(err) + } + if resp.StatusCode/100 != 2 { + panic(fmt.Sprintf("[%d]: %s", resp.StatusCode, string(readAllNoErr(resp.Body)))) + } + fmt.Println("CHECKING 10 mb influx") + + //CHCLient, err := adapter.NewClient(context.Background(), &config.Cloki.Setting.DATABASE_DATA[0], true) + CHCLient, err := ch_wrapper.NewSmartDatabaseAdapter(&config.Cloki.Setting.DATABASE_DATA[0], true) + if err != nil { + panic(err) + } + var fp uint64 + + err = CHCLient.GetFirst(fmt.Sprintf(`SELECT fingerprint FROM time_series_array_v2 WHERE +has(labels, ('format', 'influxjson')) AND has(labels, ('test_id', '%s')) AND has(labels,('type', 'logs')) AND org_id = '1' LIMIT 1`, + strconv.FormatUint(testId, 10)), &fp) + if err != nil { + panic(err) + } + + rows, err := CHCLient.GetList(fmt.Sprintf(`SELECT formatRow('TSV', string, value) FROM samples_v4 +WHERE fingerprint = %d AND org_id = '1' AND timestamp_ns >= %d AND timestamp_ns <= %d ORDER BY timestamp_ns ASC`, + fp, startTS, endTS)) + if err != nil { + panic(err) + } + j := 0 + for _, row := range rows { + row = strings.Trim(row, " \t\r\n") + expected := fmt.Sprintf("message=\"this is a very very long test string #%d\" value=%d\t0", j, j) + if row != expected { + panic(fmt.Sprintf("influx error: `%s` != `%s`", row, expected)) + } + j++ + } + if j != logsCnt { + t.Fatalf("inclux error: ingested strings number %d != %d", j, logsCnt) + } + fmt.Println("SENDING 10 mb influx json OK") +} + +func readAllNoErr(reader io.Reader) []byte { + res, _ := io.ReadAll(reader) + return res +} + +func testUsageCounting(t *testing.T) { + fmt.Println("TESTING USAGE COUNTING") + //CHClient, err := adapter.NewClient(context.Background(), &config.Cloki.Setting.DATABASE_DATA[0], true) + client, err := ch_wrapper.NewSmartDatabaseAdapterWithDSN(config.Cloki.Setting.AnalyticsDatabase, true) + if err != nil { + panic(err) + } + data_client, err := ch_wrapper.NewSmartDatabaseAdapter(&config.Cloki.Setting.DATABASE_DATA[0], true) + var org_stats [2][4]uint64 + var org_real_stats [2][4]uint64 + var orgids [2]string = [2]string{"0", "1"} + for i := 0; i < 2; i++ { + err = client.Scan(context.Background(), ` +SELECT + sum(logs_bytes_written), + sum(metrics_bytes_written), + sum(traces_bytes_written), + bitmapCardinality(groupBitmapOrState(fingerprints_written)) +FROM writer_usage_agg +WHERE org_id = $1`, []any{orgids[i]}, &org_stats[i][0], &org_stats[i][1], &org_stats[i][2], &org_stats[i][3]) + if err != nil { + panic(err) + } + fmt.Printf("Org %s: logs=%d, metrics=%d, traces=%d, fingerprints=%d\n", + orgids[i], org_stats[i][0], org_stats[i][1], org_stats[i][2], org_stats[i][3]) + + err = data_client.Scan(context.Background(), + "SELECT sum(length(string)+16) from samples_v4 WHERE string != '' AND org_id = $1", + []any{orgids[i]}, + &org_real_stats[i][0]) + if err != nil { + panic(err) + } + err = data_client.Scan(context.Background(), + "SELECT count() * 24 from samples_v4 WHERE string == '' AND org_id = $1", + []any{orgids[i]}, + &org_real_stats[i][1]) + if err != nil { + panic(err) + } + err = data_client.Scan(context.Background(), + "SELECT sum(length(payload)) from tempo_traces WHERE oid = $1", + []any{orgids[i]}, + &org_real_stats[i][2]) + if err != nil { + panic(err) + } + err = data_client.Scan(context.Background(), + "SELECT count(distinct fingerprint) from time_series_v2 WHERE org_id = $1", + []any{orgids[i]}, + &org_real_stats[i][3]) + if err != nil { + panic(err) + } + fmt.Printf("Org %s: real_logs=%d, real_metrics=%d, real_traces=%d, real_fingerprints=%d\n", + orgids[i], org_real_stats[i][0], org_real_stats[i][1], org_real_stats[i][2], org_real_stats[i][3]) + for j := 0; j < 4; j++ { + if uint64(float64(org_stats[i][j])*0.9) > org_real_stats[i][j] { + t.Fatalf("Org %s: stats mismatch at %d: expected %d, got %d", orgids[i], j, org_real_stats[i][j], org_stats[i][j]) + } + } + } +} diff --git a/writer/http/http.go b/writer/http/http.go new file mode 100644 index 00000000..fcd5da58 --- /dev/null +++ b/writer/http/http.go @@ -0,0 +1,143 @@ +package apihttp + +import ( + "context" + controllerv1 "github.com/metrico/qryn/writer/controller" + "github.com/metrico/qryn/writer/utils/logger" + "net/http" + "strings" + "time" +) + +type MiddlewareFunc func(w http.ResponseWriter, r *http.Request) error + +type Router struct { + routes map[string]map[string]http.HandlerFunc // method -> path -> handler + AuthMiddleware MiddlewareFunc +} + +func NewRouter() *Router { + return &Router{ + routes: make(map[string]map[string]http.HandlerFunc), + } +} + +// Define RouterHandleFunc to wrap router.HandleFunc with logging middleware +func (router *Router) RouterHandleFunc(method string, path string, controller controllerv1.Requester) { + router.HandleFunc(method, path, LogStatusMiddleware(func(w http.ResponseWriter, r *http.Request) { + err := controller(r, w) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + })) +} + +func (router *Router) HandleFunc(method, path string, handlerFunc http.HandlerFunc) { + if _, ok := router.routes[method]; !ok { + router.routes[method] = make(map[string]http.HandlerFunc) + } + router.routes[method][path] = handlerFunc +} + +func (router *Router) Handle(method, path string, handler http.HandlerFunc) { + router.HandleFunc(method, path, handler) +} + +// ServeHTTP handles incoming HTTP requests and routes them to the appropriate handler based on the registered routes. +func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if handlers, ok := router.routes[r.Method]; ok { + for routePath, handler := range handlers { + // Check if the request path matches the registered route path + params, match := matchPath(routePath, r.URL.Path) + if match { + // Set parameters in the request context + r = r.WithContext(context.WithValue(r.Context(), "params", params)) + + // Call the handler function + if router.AuthMiddleware != nil { + if err := router.AuthMiddleware(w, r); err != nil { + logger.Error("Auth middleware failed: ", err) + return + } + } + handler(w, r) + return + } + } + } + http.Error(w, "404 page not found", http.StatusNotFound) +} + +// matchPath matches a request path to a registered route path with parameters. +// It returns a map of parameter names to values and a boolean indicating if the paths match. +func matchPath(routePath, requestPath string) (map[string]string, bool) { + routeParts := strings.Split(routePath, "/") + requestParts := strings.Split(requestPath, "/") + + if len(routeParts) != len(requestParts) { + return nil, false + } + + params := make(map[string]string) + for i, part := range routeParts { + if strings.HasPrefix(part, ":") { + params[strings.TrimPrefix(part, ":")] = requestParts[i] + } else if part != requestParts[i] { + return nil, false + } + } + + return params, true +} + +// LogStatusMiddleware is a middleware function to capture and log the status code +func LogStatusMiddleware(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + // Create a custom ResponseWriter to capture the status code + statusWriter := &statusResponseWriter{ResponseWriter: w} + + // Call the next handler with the custom ResponseWriter + next(statusWriter, r) + // Calculate the duration + duration := time.Since(start) + + // Log information about the incoming request using logrus + logInFO := logger.LogInfo{ + "[code]": statusWriter.Status(), + "method": r.Method, + "path": r.URL.Path, + "query": r.URL.RawQuery, + "duration": duration, + } + // Log response headers + headers := make(map[string]string) + for key, values := range w.Header() { + headers[key] = strings.Join(values, ", ") + } + + // Assign response headers to logInFO + logInFO["response_headers"] = headers + + // Log the entire information + logger.Info("HTTP request", logInFO) + } +} + +// statusResponseWriter is a custom ResponseWriter to capture the status code +type statusResponseWriter struct { + http.ResponseWriter + statusCode int +} + +// WriteHeader captures the status code +func (w *statusResponseWriter) WriteHeader(statusCode int) { + w.statusCode = statusCode + w.ResponseWriter.WriteHeader(statusCode) +} + +// Status returns the captured status code +func (w *statusResponseWriter) Status() int { + return w.statusCode +} diff --git a/writer/main_dev.go b/writer/main_dev.go new file mode 100644 index 00000000..0fd69b57 --- /dev/null +++ b/writer/main_dev.go @@ -0,0 +1,47 @@ +package writer + +import ( + "github.com/gorilla/mux" + clconfig "github.com/metrico/cloki-config" + "github.com/metrico/qryn/writer/ch_wrapper" + "github.com/metrico/qryn/writer/config" + controllerv1 "github.com/metrico/qryn/writer/controller" + "github.com/metrico/qryn/writer/model" + "github.com/metrico/qryn/writer/plugin" + "github.com/metrico/qryn/writer/service/impl" + "github.com/metrico/qryn/writer/utils/numbercache" +) + +// params for Services +type ServicesObject struct { + databaseNodeMap []model.DataDatabasesMap + dbv2Map []ch_wrapper.IChClient + dbv3Map []ch_wrapper.IChClient + mainNode string +} + +var servicesObject ServicesObject +var goCache numbercache.ICache[uint64] + +func Init(cfg *clconfig.ClokiConfig, router *mux.Router) { + /* first check admin flags */ + config.Cloki = cfg + + var factory plugin.InsertServiceFactory + + factory = &impl.DevInsertServiceFactory{} + + qrynPlugin := &plugin.QrynWriterPlugin{} + + qrynPlugin.Initialize(*config.Cloki.Setting) + qrynPlugin.CreateStaticServiceRegistry(*config.Cloki.Setting, factory) + + go qrynPlugin.StartPushStat() + controllerv1.Registry = plugin.ServiceRegistry + controllerv1.FPCache = plugin.GoCache + + proMiddlewareConfig := controllerv1.NewMiddlewareConfig(controllerv1.WithExtraMiddlewareDefault...) + tempoMiddlewareConfig := controllerv1.NewMiddlewareConfig(controllerv1.WithExtraMiddlewareTempo...) + + qrynPlugin.RegisterRoutes(*config.Cloki.Setting, proMiddlewareConfig, tempoMiddlewareConfig, router) +} diff --git a/writer/metric/metric.go b/writer/metric/metric.go new file mode 100644 index 00000000..c763d3a4 --- /dev/null +++ b/writer/metric/metric.go @@ -0,0 +1,70 @@ +package metric + +import ( + "os" + "os/signal" + "runtime" + "syscall" + + "github.com/metrico/qryn/writer/model" + "github.com/metrico/qryn/writer/utils/logger" +) + +type Metric struct { + H MetricHandler + Chan chan *model.PrometheusMetric + quit chan bool +} + +type MetricHandler interface { + setup() error + reload() + expose(chan *model.PrometheusMetric) +} + +func New(name string) *Metric { + var register = map[string]MetricHandler{ + "prometheus": new(Prometheus), + } + + return &Metric{ + H: register[name], + quit: make(chan bool), + } +} + +func (m *Metric) Run() error { + err := m.H.setup() + if err != nil { + return err + } + + for i := 0; i < runtime.NumCPU(); i++ { + go func() { + m.H.expose(m.Chan) + }() + } + + s := make(chan os.Signal, 1) + signal.Notify(s, syscall.SIGHUP) + go func() { + for { + select { + case <-s: + m.H.reload() + case <-m.quit: + m.quit <- true + return + } + } + }() + + return nil +} + +func (m *Metric) End() { + m.quit <- true + <-m.quit + close(m.Chan) + logger.Info("close metric channel") +} diff --git a/writer/metric/prometheus.go b/writer/metric/prometheus.go new file mode 100644 index 00000000..f6698bea --- /dev/null +++ b/writer/metric/prometheus.go @@ -0,0 +1,62 @@ +package metric + +import ( + "fmt" + "github.com/metrico/qryn/writer/config" + "github.com/metrico/qryn/writer/model" + "strings" + "sync" + + "github.com/VictoriaMetrics/fastcache" + "github.com/metrico/qryn/writer/utils/logger" +) + +const ( + invite = "INVITE" + register = "REGISTER" + cacheSize = 60 * 1024 * 1024 +) + +// Prometheus todo Need to remove prometheus.go +type Prometheus struct { + TargetEmpty bool + TargetIP []string + TargetName []string + TargetMap map[string]string + TargetConf *sync.RWMutex + cache *fastcache.Cache +} + +func (p *Prometheus) expose(metrics chan *model.PrometheusMetric) { + //TODO implement me + panic("implement me") +} + +func (p *Prometheus) setup() (err error) { + p.TargetConf = new(sync.RWMutex) + p.TargetIP = strings.Split(cutSpace(config.Cloki.Setting.PROMETHEUS_CLIENT.TargetIP), ",") + p.TargetName = strings.Split(cutSpace(config.Cloki.Setting.PROMETHEUS_CLIENT.PushName), ",") + p.cache = fastcache.New(cacheSize) + + if len(p.TargetIP) == len(p.TargetName) && p.TargetIP != nil && p.TargetName != nil { + if len(p.TargetIP[0]) == 0 || len(p.TargetName[0]) == 0 { + logger.Info("expose metrics without or unbalanced targets") + p.TargetIP[0] = "" + p.TargetName[0] = "" + p.TargetEmpty = true + } else { + for i := range p.TargetName { + logger.Info("prometheus tag assignment %d: %s -> %s", i+1, p.TargetIP[i], p.TargetName[i]) + } + p.TargetMap = make(map[string]string) + for i := 0; i < len(p.TargetName); i++ { + p.TargetMap[p.TargetIP[i]] = p.TargetName[i] + } + } + } else { + logger.Info("please give every PromTargetIP a unique IP and PromTargetName a unique name") + return fmt.Errorf("faulty PromTargetIP or PromTargetName") + } + + return err +} diff --git a/writer/metric/reload.go b/writer/metric/reload.go new file mode 100644 index 00000000..bc064cf1 --- /dev/null +++ b/writer/metric/reload.go @@ -0,0 +1,66 @@ +package metric + +import ( + "io/ioutil" + "strings" + "unicode" + + "github.com/metrico/qryn/writer/config" + "github.com/metrico/qryn/writer/utils/logger" +) + +// remove reload.gp +func cutSpace(str string) string { + return strings.Map(func(r rune) rune { + if unicode.IsSpace(r) { + return -1 + } + return r + }, str) +} + +func (p *Prometheus) reload() { + var fsTargetIP []string + var fsTargetName []string + + fb, err := ioutil.ReadFile(config.NAME_APPLICATION) + if err != nil { + logger.Error("%v", err) + return + } + + fs := cutSpace(string(fb)) + + if si := strings.Index(fs, "PromTargetIP=\""); si > -1 { + s := si + len("PromTargetIP=\"") + e := strings.Index(fs[s:], "\"") + if e >= 7 { + fsTargetIP = strings.Split(fs[s:s+e], ",") + } + } + if si := strings.Index(fs, "PromTargetName=\""); si > -1 { + s := si + len("PromTargetName=\"") + e := strings.Index(fs[s:], "\"") + if e > 0 { + fsTargetName = strings.Split(fs[s:s+e], ",") + } + } + + if fsTargetIP != nil && fsTargetName != nil && len(fsTargetIP) == len(fsTargetName) { + p.TargetConf.Lock() + p.TargetIP = fsTargetIP + p.TargetName = fsTargetName + p.TargetEmpty = false + p.TargetMap = make(map[string]string) + for i := 0; i < len(p.TargetName); i++ { + p.TargetMap[p.TargetIP[i]] = p.TargetName[i] + } + p.TargetConf.Unlock() + logger.Info("successfully reloaded PromTargetIP: %#v", fsTargetIP) + logger.Info("successfully reloaded PromTargetName: %#v", fsTargetName) + } else { + logger.Info("failed to reload PromTargetIP: %#v", fsTargetIP) + logger.Info("failed to reload PromTargetName: %#v", fsTargetName) + logger.Info("please give every PromTargetIP a unique IP and PromTargetName a unique name") + } +} diff --git a/writer/metric/variabels.go b/writer/metric/variabels.go new file mode 100644 index 00000000..a6482d8b --- /dev/null +++ b/writer/metric/variabels.go @@ -0,0 +1,40 @@ +package metric + +import ( + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" +) + +var ( + JsonParseErrors = promauto.NewCounter(prometheus.CounterOpts{ + Name: "json_parse_errors_count", + Help: "The total number of JSON parse errors", + }) + ConnectionResetByPeer = promauto.NewCounter(prometheus.CounterOpts{ + Name: "connection_reset_by_peer_count", + Help: "The total number of connections reset by peer", + }) + SentRows = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "sent_rows", + Help: "The total number of rows sent", + }, []string{"service"}) + SentBytes = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "sent_bytes", + Help: "The total number of bytes sent", + }, []string{"service"}) + TxCloseTime = promauto.NewHistogram(prometheus.HistogramOpts{ + Name: "tx_close_time_ms", + Help: "Transaction close time in milliseconds", + // Buckets: prometheus.LinearBuckets(100, 100, 6), // Start at 100, increment by 100, and create 6 buckets + Buckets: []float64{100, 200, 500, 1000, 5000, 10000}, + }) + SendTime = promauto.NewSummary(prometheus.SummaryOpts{ + Name: "send_time_ms", + Help: "Send time in milliseconds", + Objectives: map[float64]float64{ + 0.25: 0.02, + 0.5: 200, + 0.75: 200, + 0.90: 200}, // Error tolerance of +/- 200ms + }) +) diff --git a/writer/model/databasesMapModel.go b/writer/model/databasesMapModel.go new file mode 100644 index 00000000..11f6be88 --- /dev/null +++ b/writer/model/databasesMapModel.go @@ -0,0 +1,27 @@ +package model + +import ( + "github.com/metrico/cloki-config/config" +) + +type DataDatabasesMap struct { + config.ClokiBaseDataBase +} + +type ConfigDatabasesMap struct { + Value string `json:"value"` + Name string `json:"name"` + Node string `json:"node"` + Host string `json:"host"` + Primary bool `json:"primary"` + Online bool `json:"online"` + URL string `json:"url"` + ProtectedTables []string `json:"-"` + SkipTables []string `json:"-"` +} + +type ConfigURLNode struct { + Name string `json:"name"` + URL string `json:"url"` + Primary bool `json:"primary"` +} diff --git a/writer/model/helper.go b/writer/model/helper.go new file mode 100644 index 00000000..43f00c67 --- /dev/null +++ b/writer/model/helper.go @@ -0,0 +1,28 @@ +package model + +type StrStr struct { + Str1 string + Str2 string +} + +type ValuesAgg struct { + ValueStr string + ValueInt64 int64 + ValueInt32 int32 +} +type ValuesArrTuple struct { + ValueStr string + FirstValueInt64 int64 + SecondValueInt64 int64 +} +type TreeRootStructure struct { + Field1 uint64 + Field2 uint64 + Field3 uint64 + ValueArrTuple []ValuesArrTuple +} + +type Function struct { + ValueInt64 uint64 + ValueStr string +} diff --git a/writer/model/httpResponseModel.go b/writer/model/httpResponseModel.go new file mode 100644 index 00000000..322ac69e --- /dev/null +++ b/writer/model/httpResponseModel.go @@ -0,0 +1,10 @@ +package model + +type HttpResponse struct { + Id string + Respone []byte + Err error + InstanceTag string + EndpointTag string + TimeStamp int64 +} diff --git a/writer/model/insertRequestModel.go b/writer/model/insertRequestModel.go new file mode 100644 index 00000000..f9d3d7f7 --- /dev/null +++ b/writer/model/insertRequestModel.go @@ -0,0 +1,217 @@ +package model + +import ( + "github.com/ClickHouse/ch-go/proto" + "time" +) + +const ( + SAMPLE_TYPE_LOG = 1 + SAMPLE_TYPE_METRIC = 2 + SAMPLE_TYPE_UNDEF = 0 +) + +// Our replacement for gofaster.ch StrCol +type StrColumn interface { + Append(v string) + AppendBytes(v []byte) + AppendArr(v []string) +} + +type ByteColumn interface { + Append(v []byte) + AppendArr(v [][]byte) +} + +type I8Column interface { + Append(v int8) + AppendArr(v []int8) +} + +type I64Column interface { + Append(v int64) + AppendArr(v []int64) +} + +type BColumn interface { + Append(v bool) + AppendArr(v []bool) +} + +type I64ArrayColumn interface { + Append(v []int64) + AppendArr(v []int64) +} + +type StrArrayColumn interface { + Append(v []string) +} + +type DateColumn interface { + Append(v time.Time) + AppendArr(v []time.Time) +} + +type DateColumnV2 interface { + Append(v time.Time) + AppendArr(v []time.Time) +} + +type UInt64Column interface { + Append(v uint64) + AppendArr(v []uint64) +} + +type UInt8Column interface { + Append(v uint8) + AppendArr(v []uint8) +} + +type UArrayInt64Column interface { + Append(v []uint64) + AppendArr(v [][]uint64) +} + +type UInt32Column interface { + Append(v uint32) + AppendArr(v []uint32) +} + +type UInt16Column interface { + Append(v uint16) + AppendArr(v []uint16) +} + +type Float64Column interface { + Append(v float64) + AppendArr(v []float64) +} +type TempoSamplesRequest struct { + TraceId ByteColumn + SpanId ByteColumn + ParentId StrColumn + Name StrColumn + TimestampNs I64Column + DurationNs I64Column + ServiceName StrColumn + PayloadType I8Column + Payload StrColumn +} + +type TempoTagsRequest struct { + Date DateColumn + Key StrColumn + Val StrColumn + TraceId ByteColumn + SpanId ByteColumn + TimestampNS I64Column + DurationNS I64Column +} + +type TimeSeriesRequest struct { + Type UInt8Column + Date DateColumn + Fingerprint UInt64Column + Labels StrColumn + Meta StrColumn + TTLDays UInt16Column +} + +type ProfileSamplesRequest struct { + TimestampNs UInt64Column + Ptype StrColumn + ServiceName StrColumn + SamplesTypesUnits *proto.ColArr[StrStr] + PeriodType StrColumn + PeriodUnit StrColumn + Tags *proto.ColArr[StrStr] + DurationNs UInt64Column + PayloadType StrColumn + Payload StrColumn + ValuesAgg *proto.ColArr[ValuesAgg] + Tree *proto.ColArr[TreeRootStructure] + Functions *proto.ColArr[Function] +} + +type ProfileData struct { + TimestampNs []uint64 + Ptype []string + ServiceName []string + SamplesTypesUnits []StrStr + PeriodType []string + PeriodUnit []string + Tags []StrStr + DurationNs []uint64 + PayloadType []string + Payload [][]byte + ValuesAgg []ValuesAgg + Tree []TreeRootStructure + Function []Function + Size int +} + +func (t *ProfileData) GetSize() int64 { + return int64(t.Size) +} + +// /////////////// +type TimeSeriesData struct { + MDate []time.Time + MLabels []string + MFingerprint []uint64 + MTTLDays []uint16 + Size int + MType []uint8 + MMeta string +} + +func (t *TimeSeriesData) GetSize() int64 { + return int64(t.Size) +} + +type TimeSamplesData struct { + MFingerprint []uint64 + MTimestampNS []int64 + MMessage []string + MValue []float64 + MTTLDays []uint16 + Size int + MType []uint8 +} + +func (t *TimeSamplesData) GetSize() int64 { + return int64(t.Size) +} + +type TempoTag struct { + MTraceId [][]byte + MSpanId [][]byte + MTimestampNs []int64 + MDurationNs []int64 + MDate []time.Time + MKey []string + MVal []string + Size int +} + +func (t *TempoTag) GetSize() int64 { + return int64(t.Size) +} + +type TempoSamples struct { + MTraceId [][]byte + MSpanId [][]byte + MTimestampNs []int64 + MDurationNs []int64 + + MParentId []string + MName []string + MServiceName []string + MPayloadType []int8 + MPayload [][]byte + Size int +} + +func (t *TempoSamples) GetSize() int64 { + return int64(t.Size) +} diff --git a/writer/model/jsonscan.go b/writer/model/jsonscan.go new file mode 100644 index 00000000..51e5576d --- /dev/null +++ b/writer/model/jsonscan.go @@ -0,0 +1,75 @@ +package model + +import ( + "database/sql/driver" + "encoding/json" + "errors" +) + +type JSONText json.RawMessage + +var emptyJSON = JSONText("{}") +var emptyArrayJSON = JSONText("[]") + +func (js JSONText) Value() (driver.Value, error) { + return js.String(), nil +} + +func (js *JSONText) Scan(value interface{}) error { + // if value is nil, false + if value == nil { + // set the value of the pointer yne to JSONText(false) + *js = emptyJSON + return nil + } + var source []byte + + switch t := value.(type) { + case string: + source = []byte(t) + case []byte: + if len(t) == 0 { + source = emptyJSON + } else { + source = t + } + case nil: + *js = emptyJSON + default: + return errors.New("Incompatible type for JSONText") + } + + *js = JSONText(append((*js)[0:0], source...)) + return nil + +} + +// MarshalJSON returns the *j as the JSON encoding of j. +func (j JSONText) MarshalJSON() ([]byte, error) { + if len(j) == 0 { + return emptyJSON, nil + } + return j, nil +} + +// UnmarshalJSON sets *j to a copy of data +func (j *JSONText) UnmarshalJSON(data []byte) error { + if j == nil { + return errors.New("JSONText: UnmarshalJSON on nil pointer") + } + *j = append((*j)[0:0], data...) + return nil +} + +// Unmarshal unmarshal's the json in j to v, as in json.Unmarshal. +func (j *JSONText) Unmarshal(v interface{}) error { + if len(*j) == 0 { + *j = emptyJSON + } + return json.Unmarshal([]byte(*j), v) +} + +// String supports pretty printing for JSONText types. +func (j JSONText) String() string { + return string(j) +} diff --git a/writer/model/metricdata.go b/writer/model/metricdata.go new file mode 100644 index 00000000..2e8580c7 --- /dev/null +++ b/writer/model/metricdata.go @@ -0,0 +1,28 @@ +package model + +import ( + "time" +) + +// Data +type PrometheusMetric struct { + Version uint32 `protobuf:"varint,1,req,name=Version" json:"Version"` + Protocol uint32 `protobuf:"varint,2,req,name=Protocol" json:"Protocol"` + SrcIP string `protobuf:"bytes,3,req,name=SrcIP" json:"SrcIP"` + DstIP string `protobuf:"bytes,4,req,name=DstIP" json:"DstIP"` + SrcPort uint32 `protobuf:"varint,5,req,name=SrcPort" json:"SrcPort"` + DstPort uint32 `protobuf:"varint,6,req,name=DstPort" json:"DstPort"` + Tsec uint32 `protobuf:"varint,7,req,name=Tsec" json:"Tsec"` + Tmsec uint32 `protobuf:"varint,8,req,name=Tmsec" json:"Tmsec"` + ProtoType uint32 `protobuf:"varint,9,req,name=ProtoType" json:"ProtoType"` + NodeID uint32 `protobuf:"varint,10,req,name=NodeID" json:"NodeID"` + NodePW string `protobuf:"bytes,11,req,name=NodePW" json:"NodePW"` + Payload string `protobuf:"bytes,12,req,name=Payload" json:"Payload"` + CID string `protobuf:"bytes,13,req,name=CID" json:"CID"` + Vlan uint32 `protobuf:"varint,14,req,name=Vlan" json:"Vlan"` + ProtoString string + Timestamp time.Time + NodeName string + TargetName string + SID string +} diff --git a/writer/model/parserResponse.go b/writer/model/parserResponse.go new file mode 100644 index 00000000..aff6262f --- /dev/null +++ b/writer/model/parserResponse.go @@ -0,0 +1,12 @@ +package model + +import "github.com/metrico/qryn/writer/utils/helpers" + +type ParserResponse struct { + Error error + TimeSeriesRequest helpers.SizeGetter + SamplesRequest helpers.SizeGetter + SpansAttrsRequest helpers.SizeGetter + SpansRequest helpers.SizeGetter + ProfileRequest helpers.SizeGetter +} diff --git a/writer/model/sampleModel.go b/writer/model/sampleModel.go new file mode 100755 index 00000000..9e7bf7c1 --- /dev/null +++ b/writer/model/sampleModel.go @@ -0,0 +1,45 @@ +package model + +func (TableSample) TableName() string { + return "samples" +} + +func (TableSample) TableEngine() string { + return "MergeTree PARTITION BY toDate(timestamp_ns / 1000) ORDER BY (fingerprint, timestamp_ms);" +} + +// swagger:model CreateUserStruct +type TableSample struct { + FingerPrint uint64 `db:"fingerprint" clickhouse:"type:UInt64" json:"fingerprint"` + // required: true + TimestampNS int64 `db:"timestamp_ns" clickhouse:"type:Int64" json:"timestamp_ns"` + // + Value float64 `db:"value" clickhouse:"type:Float64" json:"value"` + // example: 10 + // required: true + String string `db:"string" clickhouse:"type:String" json:"string"` +} + +type TableMetrics struct { + FingerPrint uint64 `db:"fingerprint" clickhouse:"type:UInt64" json:"fingerprint"` + // required: true + TimestampNS int64 `db:"timestamp_ns" clickhouse:"type:Int64" json:"timestamp_ns"` + // + Value float64 `db:"value" clickhouse:"type:Float64" json:"value"` +} + +/* +CREATE TABLE cloki.samples +( + `fingerprint` UInt64, + `timestamp_ms` Int64, + `value` Float64, + `string` String +) +ENGINE = MergeTree +PARTITION BY toRelativeHourNum(toDateTime(timestamp_ms / 1000)) +ORDER BY (fingerprint, timestamp_ms) +TTL toDateTime(timestamp_ms / 1000) + toIntervalDay(7) +SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1, merge_with_ttl_timeout = 3600 + +*/ diff --git a/writer/model/serviceBase.go b/writer/model/serviceBase.go new file mode 100644 index 00000000..f906ce22 --- /dev/null +++ b/writer/model/serviceBase.go @@ -0,0 +1,17 @@ +package model + +import ( + "github.com/metrico/qryn/writer/ch_wrapper" + "time" +) + +type InsertServiceOpts struct { + //Session IChClientFactory + Session ch_wrapper.IChClientFactory + Node *DataDatabasesMap + Interval time.Duration + MaxQueueSize int64 + OnBeforeInsert func() + ParallelNum int + AsyncInsert bool +} diff --git a/writer/model/streams.go b/writer/model/streams.go new file mode 100755 index 00000000..e423b66c --- /dev/null +++ b/writer/model/streams.go @@ -0,0 +1,73 @@ +package model + +import ( + "fmt" + "strconv" + "strings" + "time" + + "github.com/metrico/qryn/writer/utils/logger" +) + +type PushRequest struct { + Streams []Stream `json:"streams"` +} +type Stream struct { + Labels string `json:"labels"` + Stream map[string]string `json:"stream"` + Entries []Entry `json:"entries"` + Values [][]string `json:"values"` +} + +// LokiTime is our magic type +type LokiTime struct { + int64 +} + +// Entry is a log entry with a timestamp. +type Entry struct { + Timestamp LokiTime `json:"ts"` + Line string `json:"line"` +} + +type LabelRules struct { + Label, Cond, Value string +} + +func FromNano(nanos int64) LokiTime { + return LokiTime{nanos} +} + +func (l *LokiTime) GetNanos() int64 { + return l.int64 +} + +// UnmarshalJSON is the method that satisfies the Unmarshaller interface +func (u *LokiTime) UnmarshalJSON(b []byte) error { + //2021-12-26T16:00:06.944Z + var err error + if b != nil { + var timestamp int64 + val, _ := strconv.Unquote(string(b)) + if strings.ContainsAny(val, ":-TZ") { + t, e := time.Parse(time.RFC3339, val) + if e != nil { + logger.Debug("ERROR unmarshaling this string: ", e.Error()) + return err + } + timestamp = (t.UTC().UnixNano()) + } else { + timestamp, err = strconv.ParseInt(val, 10, 64) + if err != nil { + logger.Debug("ERROR unmarshaling this NS: ", val, err) + return err + } + } + u.int64 = timestamp + return nil + } else { + err = fmt.Errorf("bad byte array for Unmarshaling") + logger.Debug("bad data: ", err) + return err + } +} diff --git a/writer/model/timeSeries.go b/writer/model/timeSeries.go new file mode 100755 index 00000000..2a92712a --- /dev/null +++ b/writer/model/timeSeries.go @@ -0,0 +1,37 @@ +package model + +import "time" + +func (TableTimeSeries) TableName() string { + return "time_series" +} + +func (TableTimeSeries) TableEngine() string { + return "ReplacingMergeTree PARTITION BY date ORDER BY fingerprint;" +} + +type TableTimeSeries struct { + Date time.Time `db:"date" clickhouse:"type:Date" json:"date"` + // required: true + FingerPrint uint64 `db:"fingerprint" clickhouse:"type:UInt64" json:"fingerprint"` + // + Labels string `db:"labels" clickhouse:"type:String" json:"value"` + // example: 10 + // required: true + Name string `db:"name" clickhouse:"type:String" json:"string"` +} + +/* +CREATE TABLE cloki.time_series +( + `date` Date, + `fingerprint` UInt64, + `labels` String, + `name` String +) +ENGINE = ReplacingMergeTree(date) +PARTITION BY date +ORDER BY fingerprint +TTL date + toIntervalDay(7) +SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1, merge_with_ttl_timeout = 3600 +*/ diff --git a/writer/model/userModel.go b/writer/model/userModel.go new file mode 100644 index 00000000..beb144ca --- /dev/null +++ b/writer/model/userModel.go @@ -0,0 +1,254 @@ +package model + +import ( + "encoding/json" + "os" + "time" +) + +var ProxyTokens = make(map[string]int64) + +func (TableUser) TableName() string { + return "users" +} + +func (TableUser) TableEngine() string { + return "ReplacingMergeTree" +} + +// swagger:model CreateUserStruct +type TableUser struct { + UUID string `db:"uuid" csv:"-" clickhouse:"type:UUID;default:generateUUIDv4()" json:"guid"` + // required: true + Version uint64 `db:"version" csv:"-" clickhouse:"type:UInt64;default:NOW();key" json:"version" validate:"required,gte=1"` + // + UserName string `db:"username" csv:"username" clickhouse:"type:String;order" json:"username" validate:"required,username"` + // example: 10 + // required: true + PartID uint16 `db:"partid" csv:"partid" clickhouse:"type:UInt16;default:10" json:"partid" validate:"required,gte=1"` + // required: true + Email string `db:"email" csv:"email" clickhouse:"type:String" json:"email" validate:"required,email"` + // required: true + Password string `db:"-" csv:"password" json:"password"` + // required: true + FirstName string `db:"firstname" csv:"firstname" clickhouse:"type:String" json:"firstname" validate:"required,min=2,ascii"` + // required: true + LastName string `db:"lastname" csv:"lastname" clickhouse:"type:String" json:"lastname"` + // required: true + // example: NOC + Department string `db:"department" csv:"department" clickhouse:"type:String" json:"department"` + // required: true + // example: admin + UserGroup string `db:"usergroup" csv:"usergroup" clickhouse:"type:String" json:"usergroup" validate:"required,alphanum"` + IsAdmin bool `db:"-" csv:"-" json:"-"` + ExternalAuth bool `db:"-" csv:"-" json:"-"` + ForcePassword bool `db:"-" csv:"-" json:"-"` + + Params JSONText `db:"params" csv:"params" clickhouse:"type:String" json:"params"` + + Hash string `db:"hash" csv:"passwordhash" clickhouse:"type:String" json:"-"` + + // required: true + CreatedAt time.Time `db:"record_datetime" csv:"-" clickhouse:"type:DateTime;default:NOW()" json:"-"` + + ExternalProfile string `db:"-" json:"-"` + + Avatar string `db:"-" json:"-"` +} + +// swagger:model UserLegacyStruct +type TableUserLegacyFormat struct { + UserName string `csv:"username" validate:"alphanum"` + // required: true + PartID uint16 `csv:"partid" validate:"required,gte=1"` + // required: true + Email string `csv:"email" validate:"required,email"` + // required: true + Password string `csv:"password"` + // required: true + FirstName string `csv:"firstname" validate:"required,alphanum"` + // required: true + LastName string `csv:"lastname" validate:"required,alphanum"` + // required: true + // example: NOC + Department string `csv:"department"` + // example: admin + UserGroup string `csv:"usergroup" validate:"required,alphanum"` + //example {} + Params string `csv:"params"` + // example: admin + PasswordHash string `csv:"passwordhash"` +} + +// swagger:model UserLoginSuccessResponse +type UserTokenSuccessfulResponse struct { + // the token + // example: JWT Token + Token string `json:"token"` + // the uuid + // example: b9f6q23a-0bde-41ce-cd36-da3dbc17ea12 + Scope string `json:"scope"` + User struct { + Admin bool `json:"admin"` + ForcePassword bool `json:"force_password"` + } `json:"user"` +} + +// swagger:model UserDetailsResponse +type UserDetailsResponse struct { + // the uuid + User struct { + Admin bool `json:"admin"` + Username string `json:"username"` + Usergroup string `json:"usergroup"` + ForcePassword bool `json:"force_password"` + } `json:"user"` +} + +// swagger:model FailureResponse +type UserTokenBadResponse struct { + // statuscode + StatusCode int `json:"statuscode"` + // errot + Error string `json:"error"` + // message + Message string `json:"message"` +} + +// swagger:model ListUsers +type GetUser struct { + // count + Count int `json:"count"` + // the data + Data []*TableUser `json:"data"` +} + +// swagger:model UserLogin +type UserloginDetails struct { + // example: admin + // required: true + Username string `json:"username" validate:"required"` + // example: sipcapture + // required: true + Password string `json:"password" validate:"required"` + // the type of the auth one would like to perform, internal/ldap + // example: internal + // required: false + Type string `json:"type" validate:"-"` +} + +// swagger:model UserSuccessResponse +type UserCreateSuccessfulResponse struct { + // data in JSON format + // + // required: true + // + // example: af72057b-2745-0a1b-b674-56586aadec57 + Data string `json:"data"` + // the message for user + // + // required: true + // example: successfully created user + Message string `json:"message"` +} + +// swagger:model UserUpdateSuccessResponse +type UserUpdateSuccessfulResponse struct { + // example: af72057b-2745-0a1b-b674-56586aadec57 + Data string `json:"data"` + // example: successfully updated user + Message string `json:"message"` +} + +// swagger:model UserDeleteSuccessResponse +type UserDeleteSuccessfulResponse struct { + // example: af72057b-2745-0a1b-b674-56586aadec57 + Data string `json:"data"` + // example: successfully deleted user + Message string `json:"message"` +} + +type HTTPAUTHResp struct { + Auth bool `json:"auth" validate:"required"` + Data TableUser `json:"data" validate:"required"` +} + +// swagger:model UserLoginSuccessResponse +type UserProxyTokenData struct { + // the token + Token string `json:"token"` + // required: true + ExpireAt time.Time `json:"expire_at"` +} + +// swagger:model CreateUserStruct +type TableUserPasswordUpdate struct { + UUID string `db:"-" csv:"-" json:"guid"` + // required: true + Password string `db:"-" csv:"password" json:"password"` + // required: true + OldPassword string `db:"-" csv:"old_password" json:"old_password"` +} + +// swagger:model CreateUserStruct +type UserObject struct { + UserName string `json:"username"` + // example: 10 + // required: true + PartID uint16 `json:"partid"` + // required: true + UserGroup string `json:"usergroup"` +} + +// swagger:model UserFileUpload +type UserFileUpload struct { + // in: formData + // swagger:file + File os.File +} + +// swagger:model UserFileDownload +type UserFileDownload struct { + // in: body + // swagger:file + File os.File +} + +// swagger:parameters UserFileResponse UserFileRequest +type UserParameterRequest struct { + // in: formData + // swagger:file + File interface{} +} + +//swagger:model TableUserList +type TableUserList struct { + Data []TableUser `json:"data"` + // example: 13 + Count int `json:"count"` +} + +//swagger:model UserGroupList +type UserGroupList struct { + // example: ["admin","user"] + Data []string `json:"data"` + // example: 13 + Count int `json:"count"` +} + +// swagger:model OAuth2TokenExchange +type OAuth2TokenExchange struct { + // example: token + // required: true + OneTimeToken string `json:"token" validate:"required"` +} + +// swagger:model OAuth2MapToken +type OAuth2MapToken struct { + AccessToken string `json:"access_token"` + Provider string `json:"provider"` + DataJson json.RawMessage `json:"datajson"` + CreateDate time.Time `json:"create_date"` + ExpireDate time.Time `json:"expire_date"` + ProfileJson json.RawMessage `json:"profile_json"` +} diff --git a/writer/plugin/common.go b/writer/plugin/common.go new file mode 100644 index 00000000..eed2b107 --- /dev/null +++ b/writer/plugin/common.go @@ -0,0 +1,23 @@ +package plugin + +import ( + "github.com/metrico/cloki-config/config" + "github.com/metrico/qryn/writer/model" + "github.com/metrico/qryn/writer/service" +) + +type InsertServiceFactory interface { + // Create the TimeSeries Insert Service + NewTimeSeriesInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 + // Similarly, create methods for other services if needed + NewSamplesInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 + NewMetricsInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 + NewTempoSamplesInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 + NewTempoTagInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 + NewProfileSamplesInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 + // Add other service creation methods here if necessary +} + +type ConfigInitializer interface { + InitializeConfig(conf *config.ClokiBaseSettingServer) error +} diff --git a/writer/plugin/qryn_writer_db.go b/writer/plugin/qryn_writer_db.go new file mode 100644 index 00000000..f79b3847 --- /dev/null +++ b/writer/plugin/qryn_writer_db.go @@ -0,0 +1,280 @@ +package plugin + +import ( + "context" + "fmt" + clickhouse_v2 "github.com/ClickHouse/clickhouse-go/v2" + "github.com/metrico/cloki-config/config" + config2 "github.com/metrico/cloki-config/config" + "github.com/metrico/qryn/writer/ch_wrapper" + "github.com/metrico/qryn/writer/model" + "github.com/metrico/qryn/writer/service" + "github.com/metrico/qryn/writer/service/registry" + "github.com/metrico/qryn/writer/utils/logger" + "github.com/metrico/qryn/writer/utils/numbercache" + "github.com/metrico/qryn/writer/watchdog" + "time" + "unsafe" +) + +var MainNode string + +const ( + ClustModeSingle = 1 + ClustModeCloud = 2 + ClustModeDistributed = 4 + ClustModeStats = 8 +) + +func initDB(dbObject *config.ClokiBaseDataBase) error { + ctx := context.Background() + //client, err := adapter.NewClient(ctx, dbObject, false) + client, err := ch_wrapper.NewSmartDatabaseAdapter(dbObject, true) + if err != nil { + return err + } + if dbObject.Name != "" && dbObject.Name != "default" { + engine := "" + if dbObject.Cloud { + engine = "ENGINE = Atomic" + } + onCluster := "" + if dbObject.ClusterName != "" { + onCluster = fmt.Sprintf("ON CLUSTER `%s`", dbObject.ClusterName) + } + err = client.Exec(ctx, fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s` %s "+engine, dbObject.Name, onCluster)) + if err != nil { + err1 := err + logger.Info("Database creation error. Retrying without the engine", err1) + err = client.Exec(ctx, fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s` %s", dbObject.Name, onCluster)) + if err != nil { + return fmt.Errorf("database creation errors: %s; %s", err1.Error(), err.Error()) + } + } + if err != nil { + return err + } + } + return nil +} + +func (p *QrynWriterPlugin) getDataDBSession(config config.ClokiBaseSettingServer) ([]model.DataDatabasesMap, []ch_wrapper.IChClient, []ch_wrapper.IChClientFactory) { + + dbNodeMap := []model.DataDatabasesMap{} + //dbv2Map := []clickhouse_v2.Conn{} + dbv2Map := []ch_wrapper.IChClient{} + //dbv3Map := []service.IChClientFactory{} + dbv3Map := []ch_wrapper.IChClientFactory{} + // Rlogs + if logger.RLogs != nil { + clickhouse_v2.WithLogs(func(log *clickhouse_v2.Log) { + logger.RLogs.Write([]byte(log.Text)) + }) + } + + for _, dbObject := range config.DATABASE_DATA { + connv2, err := ch_wrapper.NewSmartDatabaseAdapter(&dbObject, true) + if err != nil { + err = p.humanReadableErrorsFromClickhouse(err) + logger.Error(fmt.Sprintf("couldn't make connection to [Host: %s, Node: %s, Port: %d]: \n", dbObject.Host, dbObject.Node, dbObject.Port), err) + continue + } + + dbv2Map = append(dbv2Map, connv2) + + dbv3Map = append(dbv3Map, func() (ch_wrapper.IChClient, error) { + connV3, err := ch_wrapper.NewSmartDatabaseAdapter(&dbObject, true) + return connV3, err + }) + //connV3, err := ch_wrapper.NewSmartDatabaseAdapter(&dbObject, true) + //dbv3Map = append(dbv3Map, connV3) + + dbNodeMap = append(dbNodeMap, + model.DataDatabasesMap{ClokiBaseDataBase: dbObject}) + + logger.Info("----------------------------------- ") + logger.Info("*** Database Session created *** ") + logger.Info("----------------------------------- ") + } + + return dbNodeMap, dbv2Map, dbv3Map +} + +func healthCheck(conn ch_wrapper.IChClient, isDistributed bool) { + tablesToCheck := []string{ + "time_series", "samples_v3", "settings", + "tempo_traces", "tempo_traces_attrs_gin", + } + distTablesToCheck := []string{ + "samples_v3_dist", " time_series_dist", + "tempo_traces_dist", "tempo_traces_attrs_gin_dist", + } + checkTable := func(table string) error { + query := fmt.Sprintf("SELECT 1 FROM %s LIMIT 1", table) + to, _ := context.WithTimeout(context.Background(), time.Second*30) + rows, err := conn.Query(to, query) + if err != nil { + return err + } + defer rows.Close() + return nil + } + for _, table := range tablesToCheck { + logger.Info("Checking ", table, " table") + err := checkTable(table) + if err != nil { + logger.Error(err) + panic(err) + } + logger.Info("Check ", table, " ok") + } + if isDistributed { + for _, table := range distTablesToCheck { + logger.Info("Checking ", table, " table") + err := checkTable(table) + if err != nil { + logger.Error(err) + panic(err) + } + logger.Info("Check ", table, " ok") + } + } +} + +func checkAll(base []config.ClokiBaseDataBase) error { + for _, dbObject := range base { + logger.Info(fmt.Sprintf("Checking %s:%d/%s", dbObject.Host, dbObject.Port, dbObject.Name)) + mode := ClustModeSingle + if dbObject.Cloud { + mode = ClustModeCloud + } + if dbObject.ClusterName != "" { + mode = mode | ClustModeDistributed + } + err := func() error { + //client, err := adapter.NewClient(context.Background(), &dbObject, true) + client, err := ch_wrapper.NewSmartDatabaseAdapter(&dbObject, true) + if err != nil { + return err + } + defer func(client ch_wrapper.IChClient) { + err := client.Close() + if err != nil { + logger.Error("Error closing client", err) + } + }(client) + return nil + }() + if err != nil { + return err + } + } + return nil +} + +func (p *QrynWriterPlugin) CreateStaticServiceRegistry(config config2.ClokiBaseSettingServer, factory InsertServiceFactory) { + databasesNodeHashMap := make(map[string]*model.DataDatabasesMap) + for _, node := range p.ServicesObject.DatabaseNodeMap { + databasesNodeHashMap[node.Node] = &node + } + + for i, node := range p.ServicesObject.DatabaseNodeMap { + if MainNode == "" || node.Primary { + MainNode = node.Node + } + + _node := node.Node + + TsSvcs[node.Node] = factory.NewTimeSeriesInsertService(model.InsertServiceOpts{ + Session: p.ServicesObject.Dbv3Map[i], + Node: &node, + Interval: time.Millisecond * time.Duration(config.SYSTEM_SETTINGS.DBTimer*1000), + ParallelNum: config.SYSTEM_SETTINGS.ChannelsTimeSeries, + AsyncInsert: node.AsyncInsert, + }) + TsSvcs[node.Node].Init() + + go TsSvcs[node.Node].Run() + + SplSvcs[node.Node] = factory.NewSamplesInsertService(model.InsertServiceOpts{ + Session: p.ServicesObject.Dbv3Map[i], + Node: &node, + Interval: time.Millisecond * time.Duration(config.SYSTEM_SETTINGS.DBTimer*1000), + ParallelNum: config.SYSTEM_SETTINGS.ChannelsSample, + AsyncInsert: node.AsyncInsert, + MaxQueueSize: int64(config.SYSTEM_SETTINGS.DBBulk), + OnBeforeInsert: func() { TsSvcs[_node].PlanFlush() }, + }) + SplSvcs[node.Node].Init() + go SplSvcs[node.Node].Run() + + MtrSvcs[node.Node] = factory.NewMetricsInsertService(model.InsertServiceOpts{ + Session: p.ServicesObject.Dbv3Map[i], + Node: &node, + Interval: time.Millisecond * time.Duration(config.SYSTEM_SETTINGS.DBTimer*1000), + ParallelNum: config.SYSTEM_SETTINGS.ChannelsSample, + AsyncInsert: node.AsyncInsert, + MaxQueueSize: int64(config.SYSTEM_SETTINGS.DBBulk), + OnBeforeInsert: func() { TsSvcs[_node].PlanFlush() }, + }) + MtrSvcs[node.Node].Init() + go MtrSvcs[node.Node].Run() + + TempoSamplesSvcs[node.Node] = factory.NewTempoSamplesInsertService(model.InsertServiceOpts{ + Session: p.ServicesObject.Dbv3Map[i], + Node: &node, + Interval: time.Millisecond * time.Duration(config.SYSTEM_SETTINGS.DBTimer*1000), + ParallelNum: config.SYSTEM_SETTINGS.ChannelsSample, + AsyncInsert: node.AsyncInsert, + MaxQueueSize: int64(config.SYSTEM_SETTINGS.DBBulk), + OnBeforeInsert: func() { TempoTagsSvcs[_node].PlanFlush() }, + }) + TempoSamplesSvcs[node.Node].Init() + go TempoSamplesSvcs[node.Node].Run() + + TempoTagsSvcs[node.Node] = factory.NewTempoTagInsertService(model.InsertServiceOpts{ + Session: p.ServicesObject.Dbv3Map[i], + Node: &node, + Interval: time.Millisecond * time.Duration(config.SYSTEM_SETTINGS.DBTimer*1000), + ParallelNum: config.SYSTEM_SETTINGS.ChannelsSample, + AsyncInsert: node.AsyncInsert, + MaxQueueSize: int64(config.SYSTEM_SETTINGS.DBBulk), + OnBeforeInsert: func() { TempoSamplesSvcs[_node].PlanFlush() }, + }) + TempoTagsSvcs[node.Node].Init() + go TempoTagsSvcs[node.Node].Run() + ProfileInsertSvcs[node.Node] = factory.NewProfileSamplesInsertService(model.InsertServiceOpts{ + Session: p.ServicesObject.Dbv3Map[i], + Node: &node, + Interval: time.Millisecond * time.Duration(config.SYSTEM_SETTINGS.DBTimer*1000), + ParallelNum: config.SYSTEM_SETTINGS.ChannelsSample, + AsyncInsert: node.AsyncInsert, + }) + ProfileInsertSvcs[node.Node].Init() + go ProfileInsertSvcs[node.Node].Run() + + table := "qryn_fingerprints" + if node.ClusterName != "" { + table += "_dist" + } + } + + ServiceRegistry = registry.NewStaticServiceRegistry(TsSvcs, SplSvcs, MtrSvcs, TempoSamplesSvcs, TempoTagsSvcs, ProfileInsertSvcs) + + GoCache = numbercache.NewCache[uint64](time.Minute*30, func(val uint64) []byte { + return unsafe.Slice((*byte)(unsafe.Pointer(&val)), 8) + }, databasesNodeHashMap) + + watchdog.Init([]service.InsertSvcMap{ + TsSvcs, + SplSvcs, + MtrSvcs, + TempoSamplesSvcs, + TempoTagsSvcs, + ProfileInsertSvcs, + }) + + //Run Prometheus Scaper + //go promscrape.RunPrometheusScraper(goCache, TsSvcs, MtrSvcs) + +} diff --git a/writer/plugin/qryn_writer_impl.go b/writer/plugin/qryn_writer_impl.go new file mode 100644 index 00000000..89fc340d --- /dev/null +++ b/writer/plugin/qryn_writer_impl.go @@ -0,0 +1,185 @@ +package plugin + +import ( + "context" + "fmt" + "github.com/gorilla/mux" + "github.com/metrico/cloki-config/config" + "github.com/metrico/qryn/writer/ch_wrapper" + controllerv1 "github.com/metrico/qryn/writer/controller" + "github.com/metrico/qryn/writer/model" + "github.com/metrico/qryn/writer/plugins" + "github.com/metrico/qryn/writer/service" + //config3 "github.com/metrico/qryn/writer/usagecounter/config" + "github.com/metrico/qryn/writer/utils/helpers" + "github.com/metrico/qryn/writer/utils/logger" + "gopkg.in/go-playground/validator.v9" + "net/http" + "runtime" + "time" + // "os" +) + +type ServicesObject struct { + DatabaseNodeMap []model.DataDatabasesMap + Dbv2Map []ch_wrapper.IChClient + Dbv3Map []ch_wrapper.IChClientFactory + MainNode string +} +type QrynWriterPlugin struct { + Conn ch_wrapper.IChClient + ServicesObject ServicesObject + Svc service.IInsertServiceV2 + DBConnWithDSN ch_wrapper.IChClient + DBConnWithXDSN ch_wrapper.IChClient + HTTPServer *http.Server +} + +var TsSvcs = make(service.InsertSvcMap) +var SplSvcs = make(service.InsertSvcMap) +var MtrSvcs = make(service.InsertSvcMap) +var TempoSamplesSvcs = make(service.InsertSvcMap) +var TempoTagsSvcs = make(service.InsertSvcMap) +var ProfileInsertSvcs = make(service.InsertSvcMap) + +//var servicesObject ServicesObject +//var usageStatsService *usage.TSStats + +// Initialize sets up the plugin with the given configuration. +func (p *QrynWriterPlugin) Initialize(config config.ClokiBaseSettingServer) error { + + logger.InitLogger() + + if config.SYSTEM_SETTINGS.CPUMaxProcs == 0 { + runtime.GOMAXPROCS(runtime.NumCPU()) + } else { + runtime.GOMAXPROCS(config.SYSTEM_SETTINGS.CPUMaxProcs) + } + + //TODO: move this all into a separate /registry module and add plugin support to support dynamic database registries + var err error + p.ServicesObject.DatabaseNodeMap, p.ServicesObject.Dbv2Map, p.ServicesObject.Dbv3Map = p.getDataDBSession(config) + p.ServicesObject.MainNode = "" + for _, node := range config.DATABASE_DATA { + if p.ServicesObject.MainNode == "" || node.Primary { + p.ServicesObject.MainNode = node.Node + } + } + + p.Conn, err = ch_wrapper.NewSmartDatabaseAdapter(&config.DATABASE_DATA[0], true) + if err != nil { + panic(err) + } + //// maintain databases + //plugins.RegisterDatabaseSessionPlugin(p.getDataDBSession) + //plugins.RegisterHealthCheckPlugin(healthCheck) + healthCheckPlugin := plugins.GetHealthCheckPlugin() + for i, dbObject := range config.DATABASE_DATA { + isDistributed := dbObject.ClusterName != "" + conn := p.ServicesObject.Dbv2Map[i] + if healthCheckPlugin != nil { + (*healthCheckPlugin)(conn, isDistributed) + } else { + healthCheck(conn, isDistributed) + } + + } + //for i, dbObject := range config.DATABASE_DATA { + // //TODO: move this into the /registry and with the plugin support + // healthCheck(p.ServicesObject.Dbv2Map[i], dbObject.ClusterName != "") + //} + + if !config.HTTP_SETTINGS.Prefork { + p.logCHSetup() + } + + poolSize := (config.SYSTEM_SETTINGS.ChannelsTimeSeries*2*2+ + config.SYSTEM_SETTINGS.ChannelsSample*2*11)* + len(config.DATABASE_DATA) + 20 + + if config.SYSTEM_SETTINGS.DynamicDatabases { + poolSize = 1000 + } + logger.Info("PoolSize: ", poolSize) + service.CreateColPools(int32(poolSize)) + + return nil + +} + +// RegisterRoutes registers the plugin routes with the provided HTTP ServeMux. +func (p *QrynWriterPlugin) RegisterRoutes(config config.ClokiBaseSettingServer, + middlewareFactory controllerv1.MiddlewareConfig, + middlewareTempoFactory controllerv1.MiddlewareConfig, + router *mux.Router) { + helpers.SetGlobalLimit(config.HTTP_SETTINGS.InputBufferMB * 1024 * 1024) + + httpURL := fmt.Sprintf("%s:%d", config.HTTP_SETTINGS.Host, config.HTTP_SETTINGS.Port) + // + + config.Validate = validator.New() + + p.performV1APIRouting(httpURL, config, middlewareFactory, middlewareTempoFactory, router) +} + +// Stop performs cleanup when the plugin is stopped. +func (p *QrynWriterPlugin) Stop() error { + + logger.Info("Stopping QrynWriterPlugin") + + // Stop the HTTP server + if p.HTTPServer != nil { + logger.Info("Shutting down HTTP server") + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) + defer cancel() + if err := p.HTTPServer.Shutdown(ctx); err != nil { + logger.Error("Failed to gracefully shut down HTTP server:", err) + return err + } + p.HTTPServer = nil + logger.Info("HTTP server successfully stopped") + } + + // Close all database connections in servicesObject + for _, db := range p.ServicesObject.Dbv2Map { + if err := db.Close(); err != nil { + logger.Error("Failed to close database connection:", err) + return err + } + } + + p.ServicesObject.Dbv2Map = nil // Clear references to the connections + + p.ServicesObject.Dbv3Map = nil // Clear references to the connections + + if p.Conn != nil { + logger.Info("Closing SmartDatabaseAdapter connection") + if err := p.Conn.Close(); err != nil { // Assuming `Close` is a valid method + logger.Error("Failed to close SmartDatabaseAdapter connection:", err) + return err + } + p.Conn = nil // Clear the reference + } + + mainNode := "" + for _, node := range p.ServicesObject.DatabaseNodeMap { + if mainNode == "" || node.Primary { + mainNode = node.Node + } + + TsSvcs[node.Node].Stop() + SplSvcs[node.Node].Stop() + MtrSvcs[node.Node].Stop() + TempoSamplesSvcs[node.Node].Stop() + TempoTagsSvcs[node.Node].Stop() + ProfileInsertSvcs[node.Node].Stop() + } + + //config3.CountService.Stop() + //if serviceRegistry != nil { + // serviceRegistry.Stop() + // serviceRegistry = nil + //} + logger.Info("All resources successfully cleaned up") + return nil +} diff --git a/writer/plugin/utils.go b/writer/plugin/utils.go new file mode 100644 index 00000000..a4f57a7d --- /dev/null +++ b/writer/plugin/utils.go @@ -0,0 +1,176 @@ +package plugin + +import ( + "context" + "fmt" + "github.com/gorilla/mux" + config2 "github.com/metrico/cloki-config/config" + "github.com/metrico/qryn/writer/ch_wrapper" + "github.com/metrico/qryn/writer/config" + controllerv1 "github.com/metrico/qryn/writer/controller" + apirouterv1 "github.com/metrico/qryn/writer/router" + "github.com/metrico/qryn/writer/service/registry" + "github.com/metrico/qryn/writer/utils/logger" + "github.com/metrico/qryn/writer/utils/numbercache" + "github.com/metrico/qryn/writer/utils/stat" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "runtime" + "strconv" + "strings" + "time" +) + +var ServiceRegistry registry.IServiceRegistry +var GoCache numbercache.ICache[uint64] + +type SetupState struct { + Version string + Type string + Shards int + SamplesChannels int + TSChannels int + Preforking bool + Forks int +} + +func (p *QrynWriterPlugin) humanReadableErrorsFromClickhouse(err error) error { + if err == nil { + return nil + } + hint := "" + if strings.Contains(err.Error(), "unexpected packet [21] from server") { + hint = "You may have misconfigured SSL connection to clickhouse. Please check the database_data.secure option" + } + if strings.Contains(err.Error(), "unexpected packet 2") { + hint = "You may have misconfigured or non-initialized database. Please check database_data.name option. " + + "It should be an existing and initialized database. In order to initialize the database please run " + + "\"/cloki-writer -config /path/to/config.json -initialize_db\"" + } + if hint == "" { + return err + } + return fmt.Errorf("%s. %s", err.Error(), hint) +} + +func (p *QrynWriterPlugin) logCHSetup() { + t := time.NewTicker(time.Hour) + go func() { + s := checkSetup(p.ServicesObject.Dbv2Map[0]) + for _, l := range s.ToLogLines() { + logger.Info(l) + } + for _ = range t.C { + s = checkSetup(p.ServicesObject.Dbv2Map[0]) + for _, l := range s.ToLogLines() { + logger.Info(l) + } + } + }() +} + +func (s SetupState) ToLogLines() []string { + shards := strconv.FormatInt(int64(s.Shards), 10) + if s.Shards == 0 { + shards = "can't retrieve" + } + return []string{ + "QRYN-WRITER SETTINGS:", + "qryn-writer version: " + s.Version, + "clickhouse setup type: " + s.Type, + "shards: " + shards, + "samples channels: " + strconv.FormatInt(int64(s.SamplesChannels), 10), + "time-series channels: " + strconv.FormatInt(int64(s.TSChannels), 10), + fmt.Sprintf("preforking: %v", s.Preforking), + "forks: " + strconv.FormatInt(int64(s.Forks), 10), + } +} + +func checkSetup(conn ch_wrapper.IChClient) SetupState { + setupType := "single-server" + if config.Cloki.Setting.DATABASE_DATA[0].ClusterName != "" && config.Cloki.Setting.DATABASE_DATA[0].Cloud { + setupType = "Distributed + Replicated" + } else if config.Cloki.Setting.DATABASE_DATA[0].ClusterName != "" { + setupType = "Distributed" + } else if config.Cloki.Setting.DATABASE_DATA[0].Cloud { + setupType = "Replicated" + } + shards := 1 + if config.Cloki.Setting.DATABASE_DATA[0].ClusterName != "" { + shards = getShardsNum(conn, config.Cloki.Setting.DATABASE_DATA[0].ClusterName) + } + forks := 1 + if config.Cloki.Setting.HTTP_SETTINGS.Prefork { + forks = runtime.GOMAXPROCS(0) + } + return SetupState{ + Version: "", + Type: setupType, + Shards: shards, + SamplesChannels: config.Cloki.Setting.SYSTEM_SETTINGS.ChannelsSample, + TSChannels: config.Cloki.Setting.SYSTEM_SETTINGS.ChannelsTimeSeries, + Preforking: config.Cloki.Setting.HTTP_SETTINGS.Prefork, + Forks: forks, + } +} + +func getShardsNum(conn ch_wrapper.IChClient, clusterName string) int { + to, _ := context.WithTimeout(context.Background(), time.Second*30) + rows, err := conn.Query(to, "select count(distinct shard_num) from system.clusters where cluster=$1", clusterName) + if err != nil { + logger.Error("[GSN001] Get shards error: ", err) + return 0 + } + defer rows.Close() + var res uint64 + rows.Next() + err = rows.Scan(&res) + if err != nil { + logger.Error("[GSN002] Get shards error: ", err) + return 0 + } + return int(res) +} + +func (p *QrynWriterPlugin) performV1APIRouting( + httpURL string, + config config2.ClokiBaseSettingServer, + middlewareFactory controllerv1.MiddlewareConfig, + middlewareTempoFactory controllerv1.MiddlewareConfig, + router *mux.Router) { + apirouterv1.RouteInsertDataApis(router, middlewareFactory) + apirouterv1.RoutePromDataApis(router, middlewareFactory) + apirouterv1.RouteElasticDataApis(router, middlewareFactory) + apirouterv1.RouteInsertTempoApis(router, middlewareTempoFactory) + apirouterv1.RouteProfileDataApis(router, middlewareFactory) + apirouterv1.RouteMiscApis(router, middlewareFactory) +} + +func (p *QrynWriterPlugin) StartPushStat() { + ticker := time.NewTicker(time.Second) + defer ticker.Stop() // Ensure ticker is stopped when function exits + + statCache := make(map[string]prometheus.Gauge) + getGauge := func(k string) prometheus.Gauge { + g, ok := statCache[k] + if !ok { + g = promauto.NewGauge(prometheus.GaugeOpts{ + Name: stat.SanitizeName(k), + }) + statCache[k] = g + } + return g + } + + for range ticker.C { + stats := stat.GetRate() + stat.ResetRate() + // Update Prometheus gauges + if config.Cloki.Setting.PROMETHEUS_CLIENT.Enable { + for k, v := range stats { + getGauge(k).Set(float64(v)) + } + } + + } +} diff --git a/writer/plugins/plugins.go b/writer/plugins/plugins.go new file mode 100644 index 00000000..dfa25856 --- /dev/null +++ b/writer/plugins/plugins.go @@ -0,0 +1,47 @@ +package plugins + +import ( + "github.com/metrico/cloki-config/config" + "github.com/metrico/qryn/writer/ch_wrapper" + "github.com/metrico/qryn/writer/model" + "github.com/metrico/qryn/writer/service" +) + +type NewTempoTracesService = func(opts model.InsertServiceOpts) service.IInsertServiceV2 +type NewSamplesInsertService = func(opts model.InsertServiceOpts) service.IInsertServiceV2 +type NewProfileInsertService = func(opts model.InsertServiceOpts) service.IInsertServiceV2 +type NewMetricInsertService = func(opts model.InsertServiceOpts) service.IInsertServiceV2 +type NewTimeSeriesInsertService = func(opts model.InsertServiceOpts) service.IInsertServiceV2 +type HealthCheck = func(conn ch_wrapper.IChClient, isDistributed bool) +type DatabaseSession = func(config config.ClokiBaseSettingServer) ([]model.DataDatabasesMap, []ch_wrapper.IChClient, []ch_wrapper.IChClientFactory) + +const ( + tracesInsertServicePlugin = "traces_insert" + samplesInsertServicePlugin = "samples_insert" + profileInsertServicePlugin = "profile_insert" + metricInsertServicePlugin = "metric_insert" + timeInsertServicePlugin = "time_insert" + HealthCheckPlugin = "health_check" + databaseSessionPlugin = "database_session" +) + +var RegisterTracesInsertServicePlugin = registerPlugin[NewTempoTracesService](tracesInsertServicePlugin) +var GetTracesInsertServicePlugin = getPlugin[NewTempoTracesService](tracesInsertServicePlugin) + +var RegisterSamplesInsertServicePlugin = registerPlugin[NewSamplesInsertService](samplesInsertServicePlugin) +var GetSamplesInsertServicePlugin = getPlugin[NewSamplesInsertService](samplesInsertServicePlugin) + +var RegisterMetricInsertServicePlugin = registerPlugin[NewMetricInsertService](metricInsertServicePlugin) +var GetMetricInsertServicePlugin = getPlugin[NewMetricInsertService](metricInsertServicePlugin) + +var RegisterTimeSeriesInsertServicePlugin = registerPlugin[NewTimeSeriesInsertService](timeInsertServicePlugin) +var GetTimeSeriesInsertServicePlugin = getPlugin[NewTimeSeriesInsertService](timeInsertServicePlugin) + +var GetHealthCheckPlugin = getPlugin[HealthCheck](HealthCheckPlugin) +var RegisterHealthCheckPlugin = registerPlugin[HealthCheck](HealthCheckPlugin) + +var RegisterDatabaseSessionPlugin = registerPlugin[DatabaseSession](databaseSessionPlugin) +var GetDatabaseSessionPlugin = getPlugin[DatabaseSession](databaseSessionPlugin) + +var RegisterProfileInsertServicePlugin = registerPlugin[NewProfileInsertService](profileInsertServicePlugin) +var GetProfileInsertServicePlugin = getPlugin[NewProfileInsertService](profileInsertServicePlugin) diff --git a/writer/plugins/registry.go b/writer/plugins/registry.go new file mode 100644 index 00000000..61968c75 --- /dev/null +++ b/writer/plugins/registry.go @@ -0,0 +1,23 @@ +package plugins + +var registry = make(map[string]any) + +func registerPlugin[T any](name string) func(plugin T) { + return func(plugin T) { + registry[name] = plugin + } +} + +func getPlugin[T any](name string) func() *T { + return func() *T { + v, ok := registry[name] + if !ok { + return nil + } + _v, ok := v.(T) + if !ok { + return nil + } + return &_v + } +} diff --git a/writer/registry/registry.go b/writer/registry/registry.go new file mode 100644 index 00000000..b2a276fb --- /dev/null +++ b/writer/registry/registry.go @@ -0,0 +1 @@ +package registry diff --git a/writer/registry/static.go b/writer/registry/static.go new file mode 100644 index 00000000..b2a276fb --- /dev/null +++ b/writer/registry/static.go @@ -0,0 +1 @@ +package registry diff --git a/writer/router/elasticRouter.go b/writer/router/elasticRouter.go new file mode 100644 index 00000000..67f896bb --- /dev/null +++ b/writer/router/elasticRouter.go @@ -0,0 +1,15 @@ +package apirouterv1 + +import ( + "github.com/gorilla/mux" + controllerv1 "github.com/metrico/qryn/writer/controller" +) + +func RouteElasticDataApis(router *mux.Router, cfg controllerv1.MiddlewareConfig) { + router.HandleFunc("/{target}/_doc", controllerv1.TargetDocV2(cfg)).Methods("POST") + router.HandleFunc("/{target}/_create/{id}", controllerv1.TargetDocV2(cfg)).Methods("POST") + router.HandleFunc("/{target}/_doc/{id}", controllerv1.TargetDocV2(cfg)).Methods("PUT") + router.HandleFunc("/{target}/_create/{id}", controllerv1.TargetDocV2(cfg)).Methods("PUT") + router.HandleFunc("/_bulk", controllerv1.TargetBulkV2(cfg)).Methods("POST") + router.HandleFunc("/{target}/_bulk", controllerv1.TargetBulkV2(cfg)).Methods("POST") +} diff --git a/writer/router/insertRouter.go b/writer/router/insertRouter.go new file mode 100644 index 00000000..4206c1d9 --- /dev/null +++ b/writer/router/insertRouter.go @@ -0,0 +1,19 @@ +package apirouterv1 + +import ( + "github.com/gorilla/mux" + controllerv1 "github.com/metrico/qryn/writer/controller" +) + +func RouteInsertDataApis(router *mux.Router, cfg controllerv1.MiddlewareConfig) { + router.HandleFunc("/loki/api/v1/push", controllerv1.PushStreamV2(cfg)).Methods("POST") + router.HandleFunc("/influx/api/v2/write", controllerv1.PushInfluxV2(cfg)).Methods("POST") + router.HandleFunc("/cf/v1/insert", controllerv1.PushCfDatadogV2(cfg)).Methods("POST") + router.HandleFunc("/api/v2/series", controllerv1.PushDatadogMetricsV2(cfg)).Methods("POST") + router.HandleFunc("/api/v2/logs", controllerv1.PushDatadogV2(cfg)).Methods("POST") + router.HandleFunc("/v1/logs", controllerv1.OTLPLogsV2(cfg)).Methods("POST") + + router.HandleFunc("/influx/api/v2/write/health", controllerv1.HealthInflux).Methods("GET") + router.HandleFunc("/influx/health", controllerv1.HealthInflux).Methods("GET") + +} diff --git a/writer/router/miscRouter.go b/writer/router/miscRouter.go new file mode 100644 index 00000000..f546e7d5 --- /dev/null +++ b/writer/router/miscRouter.go @@ -0,0 +1,18 @@ +package apirouterv1 + +import ( + "github.com/gorilla/mux" + controllerv1 "github.com/metrico/qryn/writer/controller" +) + +func RouteMiscApis(router *mux.Router, cfg controllerv1.MiddlewareConfig) { + + //// todo need to remove below commented code + //handler := promhttp.Handler() + //router.RouterHandleFunc(http.MethodGet, "/ready", controllerv1.Ready) + //router.RouterHandleFunc(http.MethodGet, "/metrics", func(r *http.Request, w http.ResponseWriter) error { + // handler.ServeHTTP(w, r) + // return nil + //}) + //router.RouterHandleFunc(http.MethodGet, "/config", controllerv1.Config) +} diff --git a/writer/router/profileRoute.go b/writer/router/profileRoute.go new file mode 100644 index 00000000..a067b22e --- /dev/null +++ b/writer/router/profileRoute.go @@ -0,0 +1,12 @@ +package apirouterv1 + +import ( + "github.com/gorilla/mux" + controllerv1 "github.com/metrico/qryn/writer/controller" +) + +func RouteProfileDataApis(router *mux.Router, cfg controllerv1.MiddlewareConfig) { + + router.HandleFunc("/ingest", controllerv1.PushProfileV2(cfg)).Methods("POST") + +} diff --git a/writer/router/promRouter.go b/writer/router/promRouter.go new file mode 100644 index 00000000..f85ef0b7 --- /dev/null +++ b/writer/router/promRouter.go @@ -0,0 +1,14 @@ +package apirouterv1 + +import ( + "github.com/gorilla/mux" + controllerv1 "github.com/metrico/qryn/writer/controller" +) + +func RoutePromDataApis(router *mux.Router, cfg controllerv1.MiddlewareConfig) { + router.HandleFunc("/v1/prom/remote/write", controllerv1.WriteStreamV2(cfg)).Methods("POST") + router.HandleFunc("/api/v1/prom/remote/write", controllerv1.WriteStreamV2(cfg)).Methods("POST") + router.HandleFunc("/prom/remote/write", controllerv1.WriteStreamV2(cfg)).Methods("POST") + router.HandleFunc("/api/prom/remote/write", controllerv1.WriteStreamV2(cfg)).Methods("POST") + router.HandleFunc("/api/prom/push", controllerv1.WriteStreamV2(cfg)).Methods("POST") +} diff --git a/writer/router/tempoRouter.go b/writer/router/tempoRouter.go new file mode 100644 index 00000000..ccc1c512 --- /dev/null +++ b/writer/router/tempoRouter.go @@ -0,0 +1,13 @@ +package apirouterv1 + +import ( + "github.com/gorilla/mux" + controllerv1 "github.com/metrico/qryn/writer/controller" +) + +func RouteInsertTempoApis(router *mux.Router, cfg controllerv1.MiddlewareConfig) { + router.HandleFunc("/tempo/spans", controllerv1.PushV2(cfg)).Methods("POST") + router.HandleFunc("/tempo/api/push", controllerv1.ClickhousePushV2(cfg)).Methods("POST") + router.HandleFunc("/api/v2/spans", controllerv1.PushV2(cfg)).Methods("POST") + router.HandleFunc("/v1/traces", controllerv1.OTLPPushV2(cfg)).Methods("POST") +} diff --git a/writer/service/colAdaptors.go b/writer/service/colAdaptors.go new file mode 100644 index 00000000..f007cf35 --- /dev/null +++ b/writer/service/colAdaptors.go @@ -0,0 +1,95 @@ +package service + +import ( + "github.com/ClickHouse/ch-go/proto" + "time" +) + +type DateAppender struct { + D *proto.ColDate +} + +func (d *DateAppender) Append(date time.Time) { + d.D.Append(date) +} + +func (d *DateAppender) AppendArr(date []time.Time) { + for _, _d := range date { + d.D.Append(_d) + } +} + +type Uint64Adaptor struct { + *proto.ColUInt64 +} + +type Uint32Adaptor struct { + *proto.ColUInt32 +} + +type UArray64Adaptor struct { + *proto.ColArr[uint64] +} + +func (u UArray64Adaptor) AppendArr(arr [][]uint64) { + for _, a := range arr { + u.ColArr.Append(a) + } +} +func (u Uint32Adaptor) AppendArr(arr []uint32) { + *u.ColUInt32 = append(*u.ColUInt32, arr...) +} + +func (u Uint64Adaptor) AppendArr(arr []uint64) { + *u.ColUInt64 = append(*u.ColUInt64, arr...) +} + +type Uint16Adaptor struct { + *proto.ColUInt16 +} + +func (u Uint16Adaptor) AppendArr(arr []uint16) { + *u.ColUInt16 = append(*u.ColUInt16, arr...) +} + +type Int64Adaptor struct { + *proto.ColInt64 +} + +type Uint8Adaptor struct { + *proto.ColUInt8 +} + +func (u Uint8Adaptor) AppendArr(arr []uint8) { + *u.ColUInt8 = append(*u.ColUInt8, arr...) +} + +func (u Int64Adaptor) AppendArr(arr []int64) { + *u.ColInt64 = append(*u.ColInt64, arr...) +} + +type FixedStrAdaptor struct { + *proto.ColFixedStr +} + +func (u FixedStrAdaptor) AppendArr(arr [][]byte) { + for _, e := range arr { + u.ColFixedStr.Append(e) + } +} + +type I8Adaptor struct { + *proto.ColInt8 +} + +func (u I8Adaptor) AppendArr(arr []int8) { + *u.ColInt8 = append(*u.ColInt8, arr...) +} + +type F64Adaptor struct { + *proto.ColFloat64 +} + +func (u F64Adaptor) AppendArr(arr []float64) { + *u.ColFloat64 = append(*u.ColFloat64, arr...) +} diff --git a/writer/service/columnPool.go b/writer/service/columnPool.go new file mode 100644 index 00000000..8f9c225a --- /dev/null +++ b/writer/service/columnPool.go @@ -0,0 +1,129 @@ +package service + +import ( + "github.com/ClickHouse/ch-go/proto" +) + +type IColPoolRes interface { + ReleaseColPoolRes() + Input() proto.InputColumn + Size() int + Reset() +} + +type PooledColumn[T proto.ColInput] struct { + Data T + Name string + onRelease func(res *PooledColumn[T]) + size func(res *PooledColumn[T]) int +} + +func (c *PooledColumn[T]) Input() proto.InputColumn { + return proto.InputColumn{ + Name: c.Name, + Data: c.Data, + } +} + +func (c *PooledColumn[T]) Value() T { + return c.Data +} + +func (c *PooledColumn[T]) ReleaseColPoolRes() { + /*res := c.resource + defer func() { + if res.CreationTime().Add(time.Minute * 5).Before(time.Now()) { + res.Destroy() + } else { + res.Release() + } + }()*/ + if c.onRelease != nil { + c.onRelease(c) + } + c.onRelease = nil + c.size = nil +} + +func (c *PooledColumn[T]) Reset() { + if c.onRelease != nil { + c.onRelease(c) + } +} + +func (c *PooledColumn[T]) Size() int { + if c.size != nil { + return c.size(c) + } + return 0 +} + +type colPool[T proto.ColInput] struct { + pool func() *PooledColumn[T] + onRelease func(res *PooledColumn[T]) + size func(res *PooledColumn[T]) int +} + +const defaultColPoolSize = 1500 + +func newColPool[T proto.ColInput](create func() T, size int32) *colPool[T] { + if size == 0 { + size = defaultColPoolSize + } + return &colPool[T]{ + pool: func() *PooledColumn[T] { + return &PooledColumn[T]{ + Name: "", + Data: create(), + } + }, + } +} + +func (c *colPool[T]) Acquire(name string) *PooledColumn[T] { + res := c.pool() + res.Name = name + res.onRelease = c.onRelease + res.size = c.size + return res +} + +func (c *colPool[T]) OnRelease(fn func(column *PooledColumn[T])) *colPool[T] { + c.onRelease = fn + return c +} + +func (c *colPool[T]) OnGetSize(fn func(column *PooledColumn[T]) int) *colPool[T] { + c.size = fn + return c +} + +// Define a new type for pooled array +type pooledArray[T proto.ColInput] struct { + Create func() []T + Data []T +} + +// Implement iColPoolRes interface for pooled array +func (p *pooledArray[T]) ReleaseColPoolRes() { + p.Data = p.Data[:0] +} + +func (p *pooledArray[T]) Input() proto.InputColumn { + // Implement as needed + return proto.InputColumn{} +} + +func (p *pooledArray[T]) Size() int { + return len(p.Data) +} + +func (p *pooledArray[T]) Reset() { + p.Data = p.Data[:0] +} + +func (c *colPool[T]) AcquireArray(name string) *pooledArray[T] { + return &pooledArray[T]{ + Create: func() []T { return make([]T, 0, defaultColPoolSize) }, + } +} diff --git a/writer/service/genericInsertService.go b/writer/service/genericInsertService.go new file mode 100644 index 00000000..31c4e1f7 --- /dev/null +++ b/writer/service/genericInsertService.go @@ -0,0 +1,649 @@ +package service + +import ( + "context" + "fmt" + fch "github.com/ClickHouse/ch-go" + "github.com/ClickHouse/ch-go/proto" + "github.com/metrico/qryn/writer/ch_wrapper" + "github.com/metrico/qryn/writer/model" + "github.com/metrico/qryn/writer/utils/helpers" + "github.com/metrico/qryn/writer/utils/logger" + "github.com/metrico/qryn/writer/utils/promise" + "github.com/metrico/qryn/writer/utils/stat" + "golang.org/x/sync/semaphore" + "math/rand" + "sync" + "sync/atomic" + "time" +) + +const ( + INSERT_MODE_DEFAULT = 1 + INSERT_MODE_SYNC = 2 + INSERT_MODE_ASYNC = 3 +) + +const ( + INSERT_STATE_IDLE = 0 + INSERT_STATE_INSERTING = 1 + INSERT_STATE_CLOSING = 2 +) + +const ( + BANDWITH_LIMIT = 50 * 1024 * 1024 +) + +type InsertRequest interface { + Rows() []interface{} + Response() chan error +} + +type TableTimeSeriesReq struct { + TimeSeries []*model.TableTimeSeries + Resp chan error +} + +type TableMetrics struct { + Metrics []*model.TableMetrics + Resp chan error +} + +type IInsertServiceV2 interface { + Run() + Stop() + Request(req helpers.SizeGetter, insertMode int) *promise.Promise[uint32] + Ping() (time.Time, error) + GetState(insertMode int) int + GetNodeName() string + Init() + PlanFlush() +} + +type requestPortion struct { + cols []IColPoolRes + //res []*chan error + res []*promise.Promise[uint32] + size int64 +} + +type InsertServiceV2 struct { + ServiceData + ID string + // V3Session func() (IChClient, error) + + onInsert func() + + V3Session ch_wrapper.IChClientFactory + DatabaseNode *model.DataDatabasesMap + AsyncInsert bool + OnBeforeInsert func() + pushInterval time.Duration + maxQueueSize int64 + serviceType string + + insertRequest string + + acquireColumns func() []IColPoolRes + processRequest func(any, []IColPoolRes) (int, []IColPoolRes, error) + + columns []IColPoolRes + size int64 + lastSend time.Time + results []*promise.Promise[uint32] + running bool + + watchdog *time.Ticker + lastRequest time.Time + ctx context.Context + cancel context.CancelFunc + + insertCtx context.Context + insertCancel context.CancelFunc + + mtx sync.Mutex + + client ch_wrapper.IChClient + + state int32 + + bandwithLimiter *semaphore.Weighted +} + +func (svc *InsertServiceV2) PlanFlush() { + svc.mtx.Lock() + defer svc.mtx.Unlock() + svc.insertCancel() +} + +func (svc *InsertServiceV2) Init() { + if svc.running { + return + } + func() { + svc.mtx.Lock() + defer svc.mtx.Unlock() + svc.watchdog = time.NewTicker(time.Second) + svc.ctx, svc.cancel = context.WithCancel(context.Background()) + svc.insertCtx, svc.insertCancel = context.WithTimeout(context.Background(), svc.pushInterval) + svc.columns = svc.acquireColumns() + svc.lastRequest = time.Now() + svc.running = true + }() +} + +func (svc *InsertServiceV2) GetNodeName() string { + return svc.DatabaseNode.Node +} + +func (svc *InsertServiceV2) timeoutContext() (context.Context, context.CancelFunc) { + return context.WithTimeout(context.Background(), time.Duration( + int64(svc.DatabaseNode.WriteTimeout)*int64(time.Second))) +} + +func (svc *InsertServiceV2) Ping() (time.Time, error) { + checkDuration := time.Duration(int64(svc.DatabaseNode.WriteTimeout)*int64(time.Second)*2) + time.Second*5 + if svc.lastRequest.Add(checkDuration).After(time.Now()) { + return svc.lastRequest, nil + } + return svc.lastRequest, fmt.Errorf("[SVC005] insert service `%s` must be in a deadlock", svc.ID) +} + +func (svc *InsertServiceV2) GetState(insertMode int) int { + return int(atomic.LoadInt32(&svc.state)) +} + +func (svc *InsertServiceV2) Run() { + for { + select { + case <-svc.watchdog.C: + svc.ping() + case <-svc.ctx.Done(): + svc.mtx.Lock() + svc.running = false + svc.watchdog.Stop() + svc.mtx.Unlock() + return + case <-svc.insertCtx.Done(): + svc.fetchLoopIteration() + } + } +} + +func (svc *InsertServiceV2) ping() { + if svc.client == nil { + return + } + if svc.lastRequest.Add(time.Second).After(time.Now()) { + return + } + to, _ := svc.timeoutContext() + err := svc.client.Ping(to) + if err != nil { + svc.client.Close() + svc.client = nil + logger.Error(fmt.Sprintf("[IS004]: %v", err)) + return + } + svc.lastRequest = time.Now() +} + +func (svc *InsertServiceV2) Stop() { + svc.cancel() +} + +type SizeGetter interface { + GetSize() int64 +} + +func (svc *InsertServiceV2) Request(req helpers.SizeGetter, insertMode int) *promise.Promise[uint32] { + //res := req.Response() + p := promise.New[uint32]() + if !svc.running { + logger.Info("service stopped") + p.Done(0, fmt.Errorf("service stopped")) + return p + } + var size int64 + size = req.GetSize() + to, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + err := svc.bandwithLimiter.Acquire(to, size) + if err != nil { + logger.Info("service overflow") + p.Done(0, fmt.Errorf("service overflow")) + return p + } + func() { + var ( + inserted int + err error + ) + svc.mtx.Lock() + defer svc.mtx.Unlock() + + inserted, svc.columns, err = svc.processRequest(req, svc.columns) + + if err != nil || inserted == 0 { + p.Done(0, err) + svc.bandwithLimiter.Release(size) + return + } + svc.size += size + if svc.maxQueueSize > 0 && svc.size > svc.maxQueueSize { + svc.insertCancel() + } + svc.results = append(svc.results, p) + }() + return p +} + +func (svc *InsertServiceV2) swapBuffers() (*requestPortion, error) { + svc.mtx.Lock() + defer svc.mtx.Unlock() + svc.insertCtx, svc.insertCancel = context.WithTimeout(context.Background(), svc.pushInterval) + if svc.size == 0 { + return nil, nil + } + columns := svc.columns + svc.columns = svc.acquireColumns() + svc.lastSend = time.Now() + size := svc.size + svc.size = 0 + results := svc.results + svc.results = nil + return &requestPortion{columns, results, size}, nil +} + +func (svc *InsertServiceV2) setState(state int) { + atomic.StoreInt32(&svc.state, int32(state)) +} + +func (svc *InsertServiceV2) fetchLoopIteration() { + if svc.client == nil { + var err error + svc.client, err = svc.V3Session() + if err != nil { + logger.Error("DB Connect error. Reconnect in 1s: ", err) + time.Sleep(time.Second) + return + } + } + + portion, err := svc.swapBuffers() + if portion == nil { + return + } + + if svc.OnBeforeInsert != nil { + svc.OnBeforeInsert() + } + + waiting := append([]*promise.Promise[uint32]{}, portion.res...) + releaseWaiting := func(err error) { + for _, w := range waiting { + w.Done(0, err) + } + } + + input := make(proto.Input, len(portion.cols)) + size := int64(0) + for i, c := range portion.cols { + input[i] = c.Input() + size += int64(svc.IngestSize(&input[i])) + } + svc.bandwithLimiter.Release(portion.size) + + svc.setState(INSERT_STATE_INSERTING) + defer svc.setState(INSERT_STATE_IDLE) + + startSending := time.Now() + lastFlush := time.Now() + rows := int64(input[0].Data.Rows()) + + to, cancel2 := context.WithTimeout(svc.ctx, time.Duration(int64(svc.DatabaseNode.WriteTimeout)*int64(time.Second))) + defer cancel2() + + err = svc.client.Do(to, fch.Query{ + Body: svc.insertRequest + " VALUES ", + Input: input, + }) + + stat.AddCompoundMetric("tx_close_time_ms", time.Now().Sub(lastFlush).Milliseconds()) + stat.AddCompoundMetric("send_time_ms", time.Now().Sub(startSending).Milliseconds()) + stat.AddSentMetrics(svc.serviceType+"_sent_rows", rows) + stat.AddSentMetrics(svc.serviceType+"_sent_bytes", size) + + svc.lastRequest = time.Now() + releaseWaiting(err) + + if err != nil { + svc.client.Close() + svc.client = nil + } + +} + +func (svc *InsertServiceV2) IngestSize(input *proto.InputColumn) int { + switch input.Data.(type) { + case *proto.ColStr: + return len(input.Data.(*proto.ColStr).Buf) + case proto.ColUInt64: + return 8 * input.Data.Rows() + case proto.ColInt64: + return 8 * input.Data.Rows() + case proto.ColDate: + return 2 * input.Data.Rows() + case proto.ColFloat64: + return 8 * input.Data.Rows() + case *proto.ColFixedStr: + return len(input.Data.(*proto.ColFixedStr).Buf) + case proto.ColInt8: + return input.Data.Rows() + case proto.ColBool: + return input.Data.Rows() + } + return 0 +} + +type InsertServiceV2RoundRobin struct { + ServiceData + //V3Session func() (IChClient, error) + + V3Session ch_wrapper.IChClientFactory + DatabaseNode *model.DataDatabasesMap + AsyncInsert bool + OnBeforeInsert func() + maxQueueSize int64 + pushInterval time.Duration + serviceType string + + insertRequest string + + acquireColumns func() []IColPoolRes + processRequest func(any, []IColPoolRes) (int, []IColPoolRes, error) + + svcNum int + running bool + + services []*InsertServiceV2 + rand *rand.Rand + mtx sync.Mutex +} + +func (svc *InsertServiceV2RoundRobin) PlanFlush() { + for _, svc := range svc.services { + svc.PlanFlush() + } +} + +func (svc *InsertServiceV2RoundRobin) GetNodeName() string { + return svc.DatabaseNode.Node +} + +func (svc *InsertServiceV2RoundRobin) GetState(insertMode int) int { + var ( + idle bool + ) + for _, svc := range svc.services { + switch svc.GetState(insertMode) { + case INSERT_STATE_INSERTING: + return INSERT_STATE_INSERTING + case INSERT_STATE_IDLE: + idle = true + } + } + if idle { + return INSERT_STATE_IDLE + } + return INSERT_STATE_CLOSING +} + +func (svc *InsertServiceV2RoundRobin) Ping() (time.Time, error) { + minTime := time.Now() + for _, svc := range svc.services { + t, err := svc.Ping() + if err != nil { + return t, err + } + if minTime.After(t) { + minTime = t + } + } + return minTime, nil +} + +func (svc *InsertServiceV2RoundRobin) init() { + if svc.services != nil { + return + } + logger.Info(fmt.Sprintf("creating %d services", svc.svcNum)) + svc.services = make([]*InsertServiceV2, svc.svcNum) + svc.rand = rand.New(rand.NewSource(time.Now().UnixNano())) + var bandwidthLimit int64 = BANDWITH_LIMIT + if svc.maxQueueSize*2 > BANDWITH_LIMIT { + bandwidthLimit = svc.maxQueueSize * 2 + } + for i := range svc.services { + svc.services[i] = &InsertServiceV2{ + ID: fmt.Sprintf("%s-%s-%v", svc.DatabaseNode.Node, svc.insertRequest, svc.AsyncInsert), + ServiceData: ServiceData{}, + V3Session: svc.V3Session, + DatabaseNode: svc.DatabaseNode, + AsyncInsert: svc.AsyncInsert, + OnBeforeInsert: svc.OnBeforeInsert, + pushInterval: svc.pushInterval, + maxQueueSize: svc.maxQueueSize, + insertRequest: svc.insertRequest, + acquireColumns: svc.acquireColumns, + processRequest: svc.processRequest, + bandwithLimiter: semaphore.NewWeighted(bandwidthLimit), + serviceType: svc.serviceType, + } + svc.services[i].Init() + } +} + +func (svc *InsertServiceV2RoundRobin) Init() { + svc.mtx.Lock() + defer svc.mtx.Unlock() + svc.init() +} + +func (svc *InsertServiceV2RoundRobin) Run() { + svc.mtx.Lock() + logger.Info("Running") + svc.init() + if svc.running { + logger.Info("Already running") + svc.mtx.Unlock() + return + } + wg := sync.WaitGroup{} + + for i := range svc.services { + wg.Add(1) + go func(i int) { + defer wg.Done() + svc.services[i].Run() + }(i) + } + svc.running = true + svc.mtx.Unlock() + wg.Wait() +} + +func (svc *InsertServiceV2RoundRobin) Stop() { + for _, _svc := range svc.services { + _svc.Stop() + } +} + +func (svc *InsertServiceV2RoundRobin) Request(req helpers.SizeGetter, insertMode int) *promise.Promise[uint32] { + var insertingSvcs []IInsertServiceV2 + var idleSvcs []IInsertServiceV2 + for _, _svc := range svc.services { + switch _svc.GetState(insertMode) { + case INSERT_STATE_INSERTING: + insertingSvcs = append(insertingSvcs, _svc) + case INSERT_STATE_IDLE: + idleSvcs = append(idleSvcs, _svc) + } + } + svc.mtx.Lock() + randomIdx := svc.rand.Float64() + svc.mtx.Unlock() + if len(insertingSvcs) > 0 { + return insertingSvcs[int(randomIdx*float64(len(insertingSvcs)))].Request(req, insertMode) + } else if len(idleSvcs) > 0 { + return idleSvcs[int(randomIdx*float64(len(idleSvcs)))].Request(req, insertMode) + } + return svc.services[int(randomIdx*float64(len(svc.services)))].Request(req, insertMode) +} + +type InsertServiceV2Multimodal struct { + ServiceData + //V3Session func() (IChClient, error) + V3Session ch_wrapper.IChClientFactory + DatabaseNode *model.DataDatabasesMap + AsyncInsert bool + PushInterval time.Duration + OnBeforeInsert func() + MaxQueueSize int64 + ServiceType string + + InsertRequest string + + AcquireColumns func() []IColPoolRes + ProcessRequest func(any, []IColPoolRes) (int, []IColPoolRes, error) + + SvcNum int + + SyncService *InsertServiceV2RoundRobin + AsyncService *InsertServiceV2RoundRobin + running bool + mtx sync.Mutex +} + +func (svc *InsertServiceV2Multimodal) PlanFlush() { + svc.SyncService.PlanFlush() + svc.AsyncService.PlanFlush() +} + +func (svc *InsertServiceV2Multimodal) GetNodeName() string { + return svc.DatabaseNode.Node +} + +func (svc *InsertServiceV2Multimodal) GetState(insertMode int) int { + switch insertMode { + case INSERT_MODE_SYNC: + return svc.SyncService.GetState(insertMode) + case INSERT_MODE_ASYNC: + return svc.AsyncService.GetState(insertMode) + } + if svc.AsyncInsert { + return svc.AsyncService.GetState(insertMode) + } + return svc.SyncService.GetState(insertMode) +} + +func (svc *InsertServiceV2Multimodal) init() { + if svc.SyncService != nil { + return + } + logger.Info(fmt.Sprintf("creating %d services", svc.SvcNum)) + svc.SyncService = &InsertServiceV2RoundRobin{ + ServiceData: ServiceData{}, + V3Session: svc.V3Session, + DatabaseNode: svc.DatabaseNode, + AsyncInsert: false, + OnBeforeInsert: svc.OnBeforeInsert, + pushInterval: svc.PushInterval, + maxQueueSize: svc.MaxQueueSize, + insertRequest: svc.InsertRequest, + acquireColumns: svc.AcquireColumns, + processRequest: svc.ProcessRequest, + svcNum: svc.SvcNum, + serviceType: svc.ServiceType, + } + svc.SyncService.Init() + svc.AsyncService = &InsertServiceV2RoundRobin{ + ServiceData: ServiceData{}, + V3Session: svc.V3Session, + DatabaseNode: svc.DatabaseNode, + AsyncInsert: true, + OnBeforeInsert: svc.OnBeforeInsert, + pushInterval: svc.PushInterval, + maxQueueSize: svc.MaxQueueSize, + insertRequest: svc.InsertRequest, + acquireColumns: svc.AcquireColumns, + processRequest: svc.ProcessRequest, + svcNum: svc.SvcNum, + serviceType: svc.ServiceType, + } + svc.AsyncService.Init() +} + +func (svc *InsertServiceV2Multimodal) Init() { + svc.mtx.Lock() + defer svc.mtx.Unlock() + svc.init() +} +func (svc *InsertServiceV2Multimodal) Run() { + svc.mtx.Lock() + logger.Info("Running") + svc.init() + if svc.running { + svc.mtx.Unlock() + return + } + wg := sync.WaitGroup{} + + wg.Add(1) + go func() { + defer wg.Done() + svc.SyncService.Run() + }() + wg.Add(1) + go func() { + defer wg.Done() + svc.AsyncService.Run() + }() + logger.Info("created service") + svc.running = true + svc.mtx.Unlock() + wg.Wait() +} + +func (svc *InsertServiceV2Multimodal) Stop() { + svc.SyncService.Stop() + svc.AsyncService.Stop() +} + +func (svc *InsertServiceV2Multimodal) Request(req helpers.SizeGetter, insertMode int) *promise.Promise[uint32] { + switch insertMode { + case INSERT_MODE_SYNC: + return svc.SyncService.Request(req, insertMode) + case INSERT_MODE_ASYNC: + return svc.AsyncService.Request(req, insertMode) + } + if svc.AsyncInsert { + return svc.SyncService.Request(req, insertMode) + } else { + return svc.SyncService.Request(req, insertMode) + } +} + +func (svc *InsertServiceV2Multimodal) Ping() (time.Time, error) { + minTime := time.Now() + for _, svc := range []IInsertServiceV2{svc.SyncService, svc.AsyncService} { + t, err := svc.Ping() + if err != nil { + return t, err + } + if minTime.After(t) { + minTime = t + } + } + return minTime, nil +} diff --git a/writer/service/helper.go b/writer/service/helper.go new file mode 100644 index 00000000..d87b6039 --- /dev/null +++ b/writer/service/helper.go @@ -0,0 +1,134 @@ +package service + +import ( + "github.com/ClickHouse/ch-go/proto" + "github.com/metrico/qryn/writer/model" +) + +type ColTupleStrStrAdapter struct { + proto.ColTuple +} + +func (c ColTupleStrStrAdapter) AppendArr(v []model.StrStr) { + for _, data := range v { + c.ColTuple[0].(*proto.ColStr).Append(data.Str1) + c.ColTuple[1].(*proto.ColStr).Append(data.Str2) + } + +} + +type ColTupleStrInt64Int32Adapter struct { + proto.ColTuple +} + +func (c ColTupleStrInt64Int32Adapter) AppendArr(v []model.ValuesAgg) { + for _, data := range v { + c.ColTuple[0].(*proto.ColStr).Append(data.ValueStr) + c.ColTuple[1].(*proto.ColInt64).Append(data.ValueInt64) + c.ColTuple[2].(*proto.ColInt32).Append(data.ValueInt32) + } +} + +type ColTupleFunctionAdapter struct { + proto.ColTuple +} + +func (c ColTupleFunctionAdapter) AppendArr(v []model.Function) { + for _, data := range v { + c.ColTuple[0].(*proto.ColUInt64).Append(data.ValueInt64) + c.ColTuple[1].(*proto.ColStr).Append(data.ValueStr) + } +} + +type ColTupleTreeAdapter struct { + proto.ColTuple +} + +func (c ColTupleTreeAdapter) AppendArr(v []model.TreeRootStructure) { + for _, data := range v { + c.ColTuple[0].(*proto.ColUInt64).Append(data.Field1) + c.ColTuple[1].(*proto.ColUInt64).Append(data.Field2) + c.ColTuple[2].(*proto.ColUInt64).Append(data.Field3) + c.ColTuple[3].(*proto.ColArr[model.ValuesArrTuple]).Append(data.ValueArrTuple) + } +} + +type ColTupleTreeValueAdapter struct { + proto.ColTuple +} + +func (c ColTupleTreeValueAdapter) AppendArr(v []model.ValuesArrTuple) { + for _, data := range v { + c.ColTuple[0].(*proto.ColStr).Append(data.ValueStr) + c.ColTuple[1].(*proto.ColInt64).Append(data.FirstValueInt64) + c.ColTuple[2].(*proto.ColInt64).Append(data.SecondValueInt64) + } +} + +func (c ColTupleTreeValueAdapter) Append(v model.ValuesArrTuple) { + c.ColTuple[0].(*proto.ColStr).Append(v.ValueStr) + c.ColTuple[1].(*proto.ColInt64).Append(v.FirstValueInt64) + c.ColTuple[2].(*proto.ColInt64).Append(v.SecondValueInt64) + +} + +func (c ColTupleTreeValueAdapter) Row(i int) model.ValuesArrTuple { + return model.ValuesArrTuple{ValueStr: c.ColTuple[0].(*proto.ColStr).Row(i), + FirstValueInt64: c.ColTuple[1].(*proto.ColInt64).Row(i), + SecondValueInt64: c.ColTuple[1].(*proto.ColInt64).Row(i), + } +} + +func (c ColTupleTreeAdapter) Append(v model.TreeRootStructure) { + c.ColTuple[0].(*proto.ColUInt64).Append(v.Field1) + c.ColTuple[1].(*proto.ColUInt64).Append(v.Field2) + c.ColTuple[2].(*proto.ColUInt64).Append(v.Field3) + c.ColTuple[3].(*proto.ColArr[model.ValuesArrTuple]).Append(v.ValueArrTuple) +} + +func (c ColTupleTreeAdapter) Row(i int) model.TreeRootStructure { + + return model.TreeRootStructure{ + Field1: c.ColTuple[0].(*proto.ColUInt64).Row(i), + Field2: c.ColTuple[1].(*proto.ColUInt64).Row(i), + Field3: c.ColTuple[2].(*proto.ColUInt64).Row(i), + ValueArrTuple: c.ColTuple[3].(*proto.ColArr[model.ValuesArrTuple]).Row(i), + } + +} + +func (c ColTupleFunctionAdapter) Append(v model.Function) { + c.ColTuple[0].(*proto.ColUInt64).Append(v.ValueInt64) + c.ColTuple[1].(*proto.ColStr).Append(v.ValueStr) + +} + +func (c ColTupleFunctionAdapter) Row(i int) model.Function { + return model.Function{ValueInt64: c.ColTuple[0].(*proto.ColUInt64).Row(i), + ValueStr: c.ColTuple[1].(*proto.ColStr).Row(i)} +} + +func (c ColTupleStrInt64Int32Adapter) Append(v model.ValuesAgg) { + + c.ColTuple[0].(*proto.ColStr).Append(v.ValueStr) + c.ColTuple[1].(*proto.ColInt64).Append(v.ValueInt64) + c.ColTuple[2].(*proto.ColInt32).Append(v.ValueInt32) + +} + +func (c ColTupleStrInt64Int32Adapter) Row(i int) model.ValuesAgg { + return model.ValuesAgg{ValueStr: c.ColTuple[0].(*proto.ColStr).Row(i), + ValueInt64: c.ColTuple[1].(*proto.ColInt64).Row(i), + ValueInt32: c.ColTuple[2].(*proto.ColInt32).Row(i)} +} + +func (c ColTupleStrStrAdapter) Append(v model.StrStr) { + c.ColTuple[0].(*proto.ColStr).Append(v.Str1) + c.ColTuple[1].(*proto.ColStr).Append(v.Str2) +} + +func (c ColTupleStrStrAdapter) Row(i int) model.StrStr { + + return model.StrStr{Str1: c.ColTuple[0].(*proto.ColStr).Row(i), + Str2: c.ColTuple[1].(*proto.ColStr).Row(i)} +} diff --git a/writer/service/iChClient.go b/writer/service/iChClient.go new file mode 100644 index 00000000..09a42fc2 --- /dev/null +++ b/writer/service/iChClient.go @@ -0,0 +1,18 @@ +package service + +import ( + "context" + "github.com/ClickHouse/ch-go" +) + +type IChClient interface { + Ping(ctx context.Context) error + Do(ctx context.Context, query ch.Query) error + IsAsyncInsert() bool + Close() error + GetDSN() string +} + +type IChClientFactory func() (IChClient, error) + +type InsertSvcMap = map[string]IInsertServiceV2 diff --git a/writer/service/impl/impl.go b/writer/service/impl/impl.go new file mode 100644 index 00000000..4c277e78 --- /dev/null +++ b/writer/service/impl/impl.go @@ -0,0 +1,32 @@ +package impl + +import ( + "github.com/metrico/qryn/writer/model" + "github.com/metrico/qryn/writer/service" +) + +type DevInsertServiceFactory struct{} + +func (f *DevInsertServiceFactory) NewTimeSeriesInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 { + return NewTimeSeriesInsertService(opts) +} + +func (f *DevInsertServiceFactory) NewSamplesInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 { + return NewSamplesInsertService(opts) +} + +func (f *DevInsertServiceFactory) NewMetricsInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 { + return NewMetricsInsertService(opts) +} + +func (f *DevInsertServiceFactory) NewTempoSamplesInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 { + return NewTempoSamplesInsertService(opts) +} + +func (f *DevInsertServiceFactory) NewTempoTagInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 { + return NewTempoTagsInsertService(opts) +} + +func (f *DevInsertServiceFactory) NewProfileSamplesInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 { + return NewProfileSamplesInsertService(opts) +} diff --git a/writer/service/impl/metricsInsertService.go b/writer/service/impl/metricsInsertService.go new file mode 100644 index 00000000..0f14ff62 --- /dev/null +++ b/writer/service/impl/metricsInsertService.go @@ -0,0 +1,98 @@ +package impl + +import ( + "fmt" + "github.com/ClickHouse/ch-go/proto" + "github.com/metrico/qryn/writer/model" + "github.com/metrico/qryn/writer/plugins" + "github.com/metrico/qryn/writer/service" + "github.com/metrico/qryn/writer/utils/logger" +) + +type MetricsAcquirer struct { + Type *service.PooledColumn[proto.ColUInt8] + Fingerprint *service.PooledColumn[proto.ColUInt64] + TimestampNS *service.PooledColumn[proto.ColInt64] + Value *service.PooledColumn[proto.ColFloat64] +} + +func (a *MetricsAcquirer) acq() *MetricsAcquirer { + service.StartAcq() + defer service.FinishAcq() + a.Type = service.UInt8Pool.Acquire("type") + a.Fingerprint = service.UInt64Pool.Acquire("fingerprint") + a.TimestampNS = service.Int64Pool.Acquire("timestamp_ns") + a.Value = service.Float64Pool.Acquire("value") + return a +} + +func (a *MetricsAcquirer) serialize() []service.IColPoolRes { + return []service.IColPoolRes{a.Type, a.Fingerprint, a.TimestampNS, a.Value} +} + +func (a *MetricsAcquirer) deserialize(res []service.IColPoolRes) *MetricsAcquirer { + a.Type, a.Fingerprint, a.TimestampNS, a.Value = + res[0].(*service.PooledColumn[proto.ColUInt8]), + res[1].(*service.PooledColumn[proto.ColUInt64]), + res[2].(*service.PooledColumn[proto.ColInt64]), + res[3].(*service.PooledColumn[proto.ColFloat64]) + return a +} + +func NewMetricsInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 { + + plugin := plugins.GetMetricInsertServicePlugin() + if plugin != nil { + return (*plugin)(opts) + } + + if opts.ParallelNum <= 0 { + opts.ParallelNum = 1 + } + tableName := "samples_v3" + if opts.Node.ClusterName != "" { + tableName += "_dist" + } + insertReq := fmt.Sprintf("INSERT INTO %s (`type`, fingerprint, timestamp_ns, value)", + tableName) + + return &service.InsertServiceV2Multimodal{ + ServiceData: service.ServiceData{}, + V3Session: opts.Session, + DatabaseNode: opts.Node, + PushInterval: opts.Interval, + SvcNum: opts.ParallelNum, + AsyncInsert: opts.AsyncInsert, + InsertRequest: insertReq, + ServiceType: "metrics", + MaxQueueSize: opts.MaxQueueSize, + OnBeforeInsert: opts.OnBeforeInsert, + AcquireColumns: func() []service.IColPoolRes { + return (&MetricsAcquirer{}).acq().serialize() + }, + ProcessRequest: func(ts any, res []service.IColPoolRes) (int, []service.IColPoolRes, error) { + metricData, ok := ts.(*model.TimeSamplesData) + if !ok { + logger.Info("invalid request metric") + return 0, nil, fmt.Errorf("invalid request metric") + } + metrics := (&MetricsAcquirer{}).deserialize(res) + _len := len(metrics.Fingerprint.Data) + for _, tn := range metricData.MType { + metrics.Type.Data.Append(tn) + } + for _, tn := range metricData.MTimestampNS { + metrics.TimestampNS.Data.Append(tn) + } + + for _, tn := range metricData.MFingerprint { + metrics.Fingerprint.Data.Append(tn) + } + for _, tn := range metricData.MValue { + metrics.Value.Data.Append(tn) + } + + return len(metrics.Fingerprint.Data) - _len, res, nil + }, + } +} diff --git a/writer/service/impl/profileInsertService.go b/writer/service/impl/profileInsertService.go new file mode 100644 index 00000000..de931eb7 --- /dev/null +++ b/writer/service/impl/profileInsertService.go @@ -0,0 +1,227 @@ +package impl + +import ( + "fmt" + "github.com/ClickHouse/ch-go/proto" + "github.com/metrico/qryn/writer/model" + "github.com/metrico/qryn/writer/plugins" + "github.com/metrico/qryn/writer/service" + "github.com/metrico/qryn/writer/utils/logger" +) + +type profileSamplesSnapshot struct { + timestampNs proto.ColUInt64 + ptype proto.ColStr + serviceName proto.ColStr + sampleTypesUnits proto.ColArr[model.StrStr] + periodType proto.ColStr + periodUnit proto.ColStr + tags proto.ColArr[model.StrStr] + durationNs proto.ColUInt64 + payloadType proto.ColStr + payload proto.ColStr + valuesAgg proto.ColArr[model.ValuesAgg] + tree proto.ColArr[model.TreeRootStructure] + functions proto.ColArr[model.Function] +} + +type profileSamplesAcquirer struct { + timestampNs *service.PooledColumn[proto.ColUInt64] + ptype *service.PooledColumn[*proto.ColStr] + serviceName *service.PooledColumn[*proto.ColStr] + sampleTypesUnits *service.PooledColumn[*proto.ColArr[model.StrStr]] + periodType *service.PooledColumn[*proto.ColStr] + periodUnit *service.PooledColumn[*proto.ColStr] + tags *service.PooledColumn[*proto.ColArr[model.StrStr]] + durationNs *service.PooledColumn[proto.ColUInt64] + payloadType *service.PooledColumn[*proto.ColStr] + payload *service.PooledColumn[*proto.ColStr] + valuesAgg *service.PooledColumn[*proto.ColArr[model.ValuesAgg]] + tree *service.PooledColumn[*proto.ColArr[model.TreeRootStructure]] + functions *service.PooledColumn[*proto.ColArr[model.Function]] +} + +func (t *profileSamplesAcquirer) acq() *profileSamplesAcquirer { + service.StartAcq() + defer service.FinishAcq() + t.timestampNs = service.UInt64Pool.Acquire("timestamp_ns") + t.ptype = service.StrPool.Acquire("type") + t.serviceName = service.StrPool.Acquire("service_name") + t.sampleTypesUnits = service.TupleStrStrPool.Acquire("sample_types_units") + t.periodType = service.StrPool.Acquire("period_type") + t.periodUnit = service.StrPool.Acquire("period_unit") + t.tags = service.TupleStrStrPool.Acquire("tags") + t.durationNs = service.UInt64Pool.Acquire("duration_ns") + t.payloadType = service.StrPool.Acquire("payload_type") + t.payload = service.StrPool.Acquire("payload") + t.valuesAgg = service.TupleStrInt64Int32Pool.Acquire("values_agg") + t.tree = service.TupleUInt64UInt64UInt64ArrPool.Acquire("tree") + t.functions = service.TupleUInt64StrPool.Acquire("functions") + + return t +} + +func (t *profileSamplesAcquirer) toIFace() []service.IColPoolRes { + return []service.IColPoolRes{ + t.timestampNs, + t.ptype, + t.serviceName, + t.sampleTypesUnits, + t.periodType, + t.periodUnit, + t.tags, + t.durationNs, + t.payloadType, + t.payload, + t.valuesAgg, + t.tree, + t.functions, + } +} + +func (t *profileSamplesAcquirer) fromIFace(iface []service.IColPoolRes) *profileSamplesAcquirer { + + t.timestampNs = iface[0].(*service.PooledColumn[proto.ColUInt64]) + t.ptype = iface[1].(*service.PooledColumn[*proto.ColStr]) + t.serviceName = iface[2].(*service.PooledColumn[*proto.ColStr]) + t.sampleTypesUnits = iface[3].(*service.PooledColumn[*proto.ColArr[model.StrStr]]) + t.periodType = iface[4].(*service.PooledColumn[*proto.ColStr]) + t.periodUnit = iface[5].(*service.PooledColumn[*proto.ColStr]) + t.tags = iface[6].(*service.PooledColumn[*proto.ColArr[model.StrStr]]) + t.durationNs = iface[7].(*service.PooledColumn[proto.ColUInt64]) + t.payloadType = iface[8].(*service.PooledColumn[*proto.ColStr]) + t.payload = iface[9].(*service.PooledColumn[*proto.ColStr]) + t.valuesAgg = iface[10].(*service.PooledColumn[*proto.ColArr[model.ValuesAgg]]) + t.tree = iface[11].(*service.PooledColumn[*proto.ColArr[model.TreeRootStructure]]) + t.functions = iface[12].(*service.PooledColumn[*proto.ColArr[model.Function]]) + return t +} + +func (t *profileSamplesAcquirer) snapshot() *profileSamplesSnapshot { + return &profileSamplesSnapshot{ + timestampNs: t.timestampNs.Data, + ptype: *t.ptype.Data, + serviceName: *t.serviceName.Data, + sampleTypesUnits: *t.sampleTypesUnits.Data, + periodType: *t.periodType.Data, + periodUnit: *t.periodUnit.Data, + tags: *t.tags.Data, + durationNs: t.durationNs.Data, + payloadType: *t.payloadType.Data, + payload: *t.payload.Data, + valuesAgg: *t.valuesAgg.Data, + tree: *t.tree.Data, + functions: *t.functions.Data, + } +} + +func (t *profileSamplesAcquirer) revert(snap *profileSamplesSnapshot) { + t.timestampNs.Data = snap.timestampNs + *t.ptype.Data = snap.ptype + *t.serviceName.Data = snap.serviceName + t.sampleTypesUnits.Data = &snap.sampleTypesUnits + *t.periodType.Data = snap.periodType + *t.periodUnit.Data = snap.periodUnit + t.tags.Data = &snap.tags + t.durationNs.Data = snap.durationNs + *t.payloadType.Data = snap.payloadType + *t.payload.Data = snap.payload + t.valuesAgg.Data = &snap.valuesAgg + t.tree.Data = &snap.tree + t.functions.Data = &snap.functions +} + +func (t *profileSamplesAcquirer) toRequest() model.ProfileSamplesRequest { + return model.ProfileSamplesRequest{ + TimestampNs: service.Uint64Adaptor{ColUInt64: &t.timestampNs.Data}, + Ptype: t.ptype.Data, + ServiceName: t.serviceName.Data, + SamplesTypesUnits: t.sampleTypesUnits.Data, + PeriodType: t.periodType.Data, + PeriodUnit: t.periodUnit.Data, + Tags: t.tags.Data, + DurationNs: service.Uint64Adaptor{ColUInt64: &t.durationNs.Data}, + PayloadType: t.payloadType.Data, + Payload: t.payload.Data, + ValuesAgg: t.valuesAgg.Data, + Tree: t.tree.Data, + Functions: t.functions.Data, + } +} + +func NewProfileSamplesInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 { + plugin := plugins.GetProfileInsertServicePlugin() + if plugin != nil { + return (*plugin)(opts) + } + if opts.ParallelNum <= 0 { + opts.ParallelNum = 1 + } + tableName := "profiles_input" + insertRequest := fmt.Sprintf("INSERT INTO %s "+ + "(timestamp_ns, type, service_name, sample_types_units, period_type, period_unit,tags, duration_ns, payload_type, payload, values_agg,tree,functions)", tableName) + return &service.InsertServiceV2Multimodal{ + ServiceData: service.ServiceData{}, + V3Session: opts.Session, + DatabaseNode: opts.Node, + PushInterval: opts.Interval, + InsertRequest: insertRequest, + SvcNum: opts.ParallelNum, + AsyncInsert: opts.AsyncInsert, + MaxQueueSize: opts.MaxQueueSize, + OnBeforeInsert: opts.OnBeforeInsert, + ServiceType: "profile", + AcquireColumns: func() []service.IColPoolRes { + return (&profileSamplesAcquirer{}).acq().toIFace() + }, + ProcessRequest: func(ts any, res []service.IColPoolRes) (int, []service.IColPoolRes, error) { + + profileSeriesData, ok := ts.(*model.ProfileData) + if !ok { + logger.Info("profileSeriesData") + return 0, nil, fmt.Errorf("invalid request samples insert") + } + acquirer := (&profileSamplesAcquirer{}).fromIFace(res) + //snap := acquirer.snapshot() + s1 := res[0].Size() + + (&service.Uint64Adaptor{ColUInt64: &acquirer.timestampNs.Data}).AppendArr(profileSeriesData.TimestampNs) + (&service.Uint64Adaptor{ColUInt64: &acquirer.durationNs.Data}).AppendArr(profileSeriesData.DurationNs) + for _, serviceName := range profileSeriesData.ServiceName { + acquirer.serviceName.Data.Append(serviceName) + } + + for _, pt := range profileSeriesData.Ptype { + acquirer.ptype.Data.Append(pt) + } + + for _, payloadType := range profileSeriesData.PayloadType { + acquirer.payloadType.Data.Append(payloadType) + } + + for _, periodUnit := range profileSeriesData.PeriodUnit { + acquirer.periodUnit.Data.Append(periodUnit) + } + + for _, periodType := range profileSeriesData.PeriodType { + acquirer.periodType.Data.Append(periodType) + } + for _, payload := range profileSeriesData.Payload { + acquirer.payload.Data.AppendBytes(payload) + } + + acquirer.sampleTypesUnits.Data.Append(profileSeriesData.SamplesTypesUnits) + acquirer.tags.Data.Append(profileSeriesData.Tags) + acquirer.valuesAgg.Data.Append(profileSeriesData.ValuesAgg) + acquirer.functions.Data.Append(profileSeriesData.Function) + acquirer.tree.Data.Append(profileSeriesData.Tree) + + //err := ts.ProfileSamples(acquirer.toRequest()) + //if err != nil { + // acquirer.revert(snap) + // return 0, acquirer.toIFace(), err + //} + return res[0].Size() - s1, acquirer.toIFace(), nil + }, + } +} diff --git a/writer/service/impl/samplesInsertService.go b/writer/service/impl/samplesInsertService.go new file mode 100644 index 00000000..45e73248 --- /dev/null +++ b/writer/service/impl/samplesInsertService.go @@ -0,0 +1,104 @@ +package impl + +import ( + "fmt" + "github.com/ClickHouse/ch-go/proto" + "github.com/metrico/qryn/writer/model" + "github.com/metrico/qryn/writer/plugins" + "github.com/metrico/qryn/writer/service" + "github.com/metrico/qryn/writer/utils/logger" +) + +type SamplesAcquirer struct { + Type *service.PooledColumn[proto.ColUInt8] + Fingerprint *service.PooledColumn[proto.ColUInt64] + TimestampNS *service.PooledColumn[proto.ColInt64] + String *service.PooledColumn[*proto.ColStr] + Value *service.PooledColumn[proto.ColFloat64] +} + +func (a *SamplesAcquirer) acq() *SamplesAcquirer { + service.StartAcq() + defer service.FinishAcq() + a.Type = service.UInt8Pool.Acquire("type") + a.Fingerprint = service.UInt64Pool.Acquire("fingerprint") + a.TimestampNS = service.Int64Pool.Acquire("timestamp_ns") + a.String = service.StrPool.Acquire("string") + a.Value = service.Float64Pool.Acquire("value") + return a +} + +func (a *SamplesAcquirer) serialize() []service.IColPoolRes { + return []service.IColPoolRes{a.Type, a.Fingerprint, a.TimestampNS, a.String, a.Value} +} + +func (a *SamplesAcquirer) deserialize(res []service.IColPoolRes) *SamplesAcquirer { + a.Type, a.Fingerprint, a.TimestampNS, a.String, a.Value = + + res[0].(*service.PooledColumn[proto.ColUInt8]), + res[1].(*service.PooledColumn[proto.ColUInt64]), + res[2].(*service.PooledColumn[proto.ColInt64]), + res[3].(*service.PooledColumn[*proto.ColStr]), + res[4].(*service.PooledColumn[proto.ColFloat64]) + return a +} + +func NewSamplesInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 { + + plugin := plugins.GetSamplesInsertServicePlugin() + if plugin != nil { + return (*plugin)(opts) + } + if opts.ParallelNum <= 0 { + opts.ParallelNum = 1 + } + table := "samples_v3" + if opts.Node.ClusterName != "" { + table += "_dist" + } + insertReq := fmt.Sprintf("INSERT INTO %s (type,fingerprint, timestamp_ns, string, value)", + table) + return &service.InsertServiceV2Multimodal{ + ServiceData: service.ServiceData{}, + V3Session: opts.Session, + DatabaseNode: opts.Node, + PushInterval: opts.Interval, + SvcNum: opts.ParallelNum, + AsyncInsert: opts.AsyncInsert, + MaxQueueSize: opts.MaxQueueSize, + OnBeforeInsert: opts.OnBeforeInsert, + InsertRequest: insertReq, + ServiceType: "samples", + AcquireColumns: func() []service.IColPoolRes { + return (&SamplesAcquirer{}).acq().serialize() + }, + ProcessRequest: func(ts any, res []service.IColPoolRes) (int, []service.IColPoolRes, error) { + timeSeriesData, ok := ts.(*model.TimeSamplesData) + if !ok { + logger.Info("NewSamplesInsertService") + return 0, nil, fmt.Errorf("invalid request samples insert") + } + samples := (&SamplesAcquirer{}).deserialize(res) + _len := len(samples.Fingerprint.Data) + + for _, timeNs := range timeSeriesData.MTimestampNS { + samples.TimestampNS.Data.Append(timeNs) + } + + for _, mf := range timeSeriesData.MFingerprint { + samples.Fingerprint.Data.Append(mf) + } + for _, mt := range timeSeriesData.MType { + samples.Type.Data.Append(mt) + } + + for _, mValue := range timeSeriesData.MValue { + samples.Value.Data.Append(mValue) + } + for _, mMessage := range timeSeriesData.MMessage { + samples.String.Data.Append(mMessage) + } + return len(samples.Fingerprint.Data) - _len, samples.serialize(), nil + }, + } +} diff --git a/writer/service/impl/tempoInsertService.go b/writer/service/impl/tempoInsertService.go new file mode 100644 index 00000000..8932052d --- /dev/null +++ b/writer/service/impl/tempoInsertService.go @@ -0,0 +1,258 @@ +package impl + +import ( + "fmt" + "github.com/ClickHouse/ch-go/proto" + "github.com/metrico/qryn/writer/model" + "github.com/metrico/qryn/writer/plugins" + "github.com/metrico/qryn/writer/service" + "github.com/metrico/qryn/writer/utils/logger" +) + +type tempoSamplesAcquirer struct { + traceId *service.PooledColumn[*proto.ColFixedStr] + spanId *service.PooledColumn[*proto.ColFixedStr] + parentId *service.PooledColumn[*proto.ColStr] + name *service.PooledColumn[*proto.ColStr] + timestampNs *service.PooledColumn[proto.ColInt64] + durationNs *service.PooledColumn[proto.ColInt64] + serviceName *service.PooledColumn[*proto.ColStr] + payloadType *service.PooledColumn[proto.ColInt8] + payload *service.PooledColumn[*proto.ColStr] +} + +func (t *tempoSamplesAcquirer) acq() *tempoSamplesAcquirer { + service.StartAcq() + defer service.FinishAcq() + + t.traceId = service.FixedStringPool.Acquire("trace_id") + t.traceId.Data.SetSize(16) + t.spanId = service.FixedStringPool.Acquire("span_id") + t.spanId.Data.SetSize(8) + t.parentId = service.StrPool.Acquire("parent_id") + t.name = service.StrPool.Acquire("name") + t.timestampNs = service.Int64Pool.Acquire("timestamp_ns") + t.durationNs = service.Int64Pool.Acquire("duration_ns") + t.serviceName = service.StrPool.Acquire("service_name") + t.payloadType = service.Int8ColPool.Acquire("payload_type") + t.payload = service.StrPool.Acquire("payload") + return t +} + +func (t *tempoSamplesAcquirer) toIFace() []service.IColPoolRes { + return []service.IColPoolRes{ + t.traceId, + t.spanId, + t.parentId, + t.name, + t.timestampNs, + t.durationNs, + t.serviceName, + t.payloadType, + t.payload, + } +} + +func (t *tempoSamplesAcquirer) fromIFace(iface []service.IColPoolRes) *tempoSamplesAcquirer { + t.traceId = iface[0].(*service.PooledColumn[*proto.ColFixedStr]) + t.spanId = iface[1].(*service.PooledColumn[*proto.ColFixedStr]) + t.parentId = iface[2].(*service.PooledColumn[*proto.ColStr]) + t.name = iface[3].(*service.PooledColumn[*proto.ColStr]) + t.timestampNs = iface[4].(*service.PooledColumn[proto.ColInt64]) + t.durationNs = iface[5].(*service.PooledColumn[proto.ColInt64]) + t.serviceName = iface[6].(*service.PooledColumn[*proto.ColStr]) + t.payloadType = iface[7].(*service.PooledColumn[proto.ColInt8]) + t.payload = iface[8].(*service.PooledColumn[*proto.ColStr]) + return t +} + +type BoolWrap struct { + bc *proto.ColBool +} + +func (b *BoolWrap) Append(v bool) { + *b.bc = append(*b.bc, v) +} + +func (t *tempoSamplesAcquirer) toRequest() model.TempoSamplesRequest { + return model.TempoSamplesRequest{ + TraceId: service.FixedStrAdaptor{ColFixedStr: t.traceId.Data}, + SpanId: service.FixedStrAdaptor{ColFixedStr: t.spanId.Data}, + ParentId: t.parentId.Data, + Name: t.name.Data, + TimestampNs: service.Int64Adaptor{ColInt64: &t.timestampNs.Data}, + DurationNs: service.Int64Adaptor{ColInt64: &t.durationNs.Data}, + ServiceName: t.serviceName.Data, + PayloadType: service.I8Adaptor{ColInt8: &t.payloadType.Data}, + Payload: t.payload.Data, + } +} + +func NewTempoSamplesInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 { + plugin := plugins.GetTracesInsertServicePlugin() + if plugin != nil { + return (*plugin)(opts) + } + if opts.ParallelNum <= 0 { + opts.ParallelNum = 1 + } + tableName := "tempo_traces" + if opts.Node.ClusterName != "" { + tableName += "_dist" + } + insertRequest := fmt.Sprintf("INSERT INTO %s "+ + "(trace_id ,span_id, parent_id, name, timestamp_ns, "+ + "duration_ns, service_name, payload_type, payload)", tableName) + return &service.InsertServiceV2Multimodal{ + ServiceData: service.ServiceData{}, + V3Session: opts.Session, + DatabaseNode: opts.Node, + PushInterval: opts.Interval, + InsertRequest: insertRequest, + SvcNum: opts.ParallelNum, + AsyncInsert: opts.AsyncInsert, + MaxQueueSize: opts.MaxQueueSize, + OnBeforeInsert: opts.OnBeforeInsert, + ServiceType: "traces", + AcquireColumns: func() []service.IColPoolRes { + return (&tempoSamplesAcquirer{}).acq().toIFace() + }, + ProcessRequest: func(v2 any, res []service.IColPoolRes) (int, []service.IColPoolRes, error) { + tempSamples, ok := v2.(*model.TempoSamples) + if !ok { + logger.Info("invalid request type tempo samples") + return 0, nil, fmt.Errorf("invalid request type tempo samples") + } + acquirer := (&tempoSamplesAcquirer{}).fromIFace(res) + s1 := res[0].Size() + (&service.FixedStrAdaptor{ColFixedStr: acquirer.traceId.Data}).AppendArr(tempSamples.MTraceId) + (&service.FixedStrAdaptor{ColFixedStr: acquirer.spanId.Data}).AppendArr(tempSamples.MSpanId) + (&service.Int64Adaptor{ColInt64: &acquirer.timestampNs.Data}).AppendArr(tempSamples.MTimestampNs) + (&service.Int64Adaptor{ColInt64: &acquirer.durationNs.Data}).AppendArr(tempSamples.MDurationNs) + acquirer.name.Data.AppendArr(tempSamples.MName) + acquirer.parentId.Data.AppendArr(tempSamples.MParentId) + for _, p := range tempSamples.MPayload { + acquirer.payload.Data.AppendBytes(p) + } + (&service.I8Adaptor{ColInt8: &acquirer.payloadType.Data}).AppendArr(tempSamples.MPayloadType) + acquirer.serviceName.Data.AppendArr(tempSamples.MServiceName) + return res[0].Size() - s1, acquirer.toIFace(), nil + }, + } +} + +type tempoTagsAcquirer struct { + date *service.PooledColumn[proto.ColDate] + key *service.PooledColumn[*proto.ColStr] + val *service.PooledColumn[*proto.ColStr] + traceId *service.PooledColumn[*proto.ColFixedStr] + spanId *service.PooledColumn[*proto.ColFixedStr] + timestampNS *service.PooledColumn[proto.ColInt64] + durationNS *service.PooledColumn[proto.ColInt64] +} + +func (t *tempoTagsAcquirer) acq() *tempoTagsAcquirer { + service.StartAcq() + defer service.FinishAcq() + t.date = service.DatePool.Acquire("date") + t.key = service.StrPool.Acquire("key") + t.val = service.StrPool.Acquire("val") + t.traceId = service.FixedStringPool.Acquire("trace_id") + t.traceId.Data.SetSize(16) + t.spanId = service.FixedStringPool.Acquire("span_id") + t.spanId.Data.SetSize(8) + t.timestampNS = service.Int64Pool.Acquire("timestamp_ns") + t.timestampNS.Data.Reset() + t.durationNS = service.Int64Pool.Acquire("duration") + t.durationNS.Data.Reset() + return t +} + +func (t *tempoTagsAcquirer) toIFace() []service.IColPoolRes { + return []service.IColPoolRes{ + t.date, + t.key, + t.val, + t.traceId, + t.spanId, + t.timestampNS, + t.durationNS, + } +} + +func (t *tempoTagsAcquirer) fromIFace(iface []service.IColPoolRes) *tempoTagsAcquirer { + t.date = iface[0].(*service.PooledColumn[proto.ColDate]) + t.key = iface[1].(*service.PooledColumn[*proto.ColStr]) + t.val = iface[2].(*service.PooledColumn[*proto.ColStr]) + t.traceId = iface[3].(*service.PooledColumn[*proto.ColFixedStr]) + t.spanId = iface[4].(*service.PooledColumn[*proto.ColFixedStr]) + t.timestampNS = iface[5].(*service.PooledColumn[proto.ColInt64]) + t.durationNS = iface[6].(*service.PooledColumn[proto.ColInt64]) + return t +} + +func (t *tempoTagsAcquirer) toRequest() model.TempoTagsRequest { + return model.TempoTagsRequest{ + Date: &service.DateAppender{D: &t.date.Data}, + Key: t.key.Data, + Val: t.val.Data, + TraceId: service.FixedStrAdaptor{ColFixedStr: t.traceId.Data}, + SpanId: service.FixedStrAdaptor{ColFixedStr: t.spanId.Data}, + TimestampNS: service.Int64Adaptor{ColInt64: &t.timestampNS.Data}, + DurationNS: service.Int64Adaptor{ColInt64: &t.durationNS.Data}, + } +} + +func NewTempoTagsInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 { + if opts.ParallelNum <= 0 { + opts.ParallelNum = 1 + } + tableName := "tempo_traces_attrs_gin" + if opts.Node.ClusterName != "" { + tableName += "_dist" + } + insertRequest := fmt.Sprintf("INSERT INTO %s (date, key, val, trace_id, span_id, timestamp_ns, duration)", + tableName) + return &service.InsertServiceV2Multimodal{ + ServiceData: service.ServiceData{}, + V3Session: opts.Session, + DatabaseNode: opts.Node, + PushInterval: opts.Interval, + InsertRequest: insertRequest, + SvcNum: opts.ParallelNum, + AsyncInsert: opts.AsyncInsert, + MaxQueueSize: opts.MaxQueueSize, + OnBeforeInsert: opts.OnBeforeInsert, + ServiceType: "traces_tags", + + AcquireColumns: func() []service.IColPoolRes { + return (&tempoTagsAcquirer{}).acq().toIFace() + }, + ProcessRequest: func(v2 any, res []service.IColPoolRes) (int, []service.IColPoolRes, error) { + tempTags, ok := v2.(*model.TempoTag) + if !ok { + return 0, nil, fmt.Errorf("invalid request tempo tags") + } + + acquirer := (&tempoTagsAcquirer{}).fromIFace(res) + s1 := res[0].Size() + (&service.FixedStrAdaptor{ColFixedStr: acquirer.traceId.Data}).AppendArr(tempTags.MTraceId) + (&service.FixedStrAdaptor{ColFixedStr: acquirer.spanId.Data}).AppendArr(tempTags.MSpanId) + (&service.Int64Adaptor{ColInt64: &acquirer.timestampNS.Data}).AppendArr(tempTags.MTimestampNs) + (&service.Int64Adaptor{ColInt64: &acquirer.durationNS.Data}).AppendArr(tempTags.MDurationNs) + acquirer.key.Data.AppendArr(tempTags.MKey) + acquirer.val.Data.AppendArr(tempTags.MVal) + (&service.DateAppender{D: &acquirer.date.Data}).AppendArr(tempTags.MDate) + return res[0].Size() - s1, acquirer.toIFace(), nil + }, + } +} + +func fastFill[T uint64 | string](val T, len int) []T { + res := make([]T, len) + res[0] = val + for c := 1; c < len; c >>= 1 { + copy(res[c:], res[:c]) + } + return res +} diff --git a/writer/service/impl/timeSeriesInsertService.go b/writer/service/impl/timeSeriesInsertService.go new file mode 100644 index 00000000..fc0a4bbb --- /dev/null +++ b/writer/service/impl/timeSeriesInsertService.go @@ -0,0 +1,95 @@ +package impl + +import ( + "fmt" + "github.com/ClickHouse/ch-go/proto" + "github.com/metrico/qryn/writer/model" + "github.com/metrico/qryn/writer/plugins" + "github.com/metrico/qryn/writer/service" + "github.com/metrico/qryn/writer/utils/logger" +) + +type TimeSeriesAcquirer struct { + Type *service.PooledColumn[proto.ColUInt8] + Date *service.PooledColumn[proto.ColDate] + Fingerprint *service.PooledColumn[proto.ColUInt64] + Labels *service.PooledColumn[*proto.ColStr] +} + +func (a *TimeSeriesAcquirer) acq() *TimeSeriesAcquirer { + service.StartAcq() + defer service.FinishAcq() + a.Type = service.UInt8Pool.Acquire("type") + a.Date = service.DatePool.Acquire("date") + a.Fingerprint = service.UInt64Pool.Acquire("fingerprint") + a.Labels = service.StrPool.Acquire("labels") + return a +} + +func (a *TimeSeriesAcquirer) serialize() []service.IColPoolRes { + return []service.IColPoolRes{a.Type, a.Date, a.Fingerprint, a.Labels} +} + +func (a *TimeSeriesAcquirer) deserialize(res []service.IColPoolRes) *TimeSeriesAcquirer { + a.Type, a.Date, a.Fingerprint, a.Labels = + res[0].(*service.PooledColumn[proto.ColUInt8]), + res[1].(*service.PooledColumn[proto.ColDate]), + res[2].(*service.PooledColumn[proto.ColUInt64]), + res[3].(*service.PooledColumn[*proto.ColStr]) + return a +} + +func NewTimeSeriesInsertService(opts model.InsertServiceOpts) service.IInsertServiceV2 { + + plugin := plugins.GetTimeSeriesInsertServicePlugin() + if plugin != nil { + return (*plugin)(opts) + } + if opts.ParallelNum <= 0 { + opts.ParallelNum = 1 + } + table := "time_series" + if opts.Node.ClusterName != "" { + table += "_dist" + } + insertReq := fmt.Sprintf("INSERT INTO %s (type, date, fingerprint, labels)", + table) + return &service.InsertServiceV2Multimodal{ + ServiceData: service.ServiceData{}, + V3Session: opts.Session, + DatabaseNode: opts.Node, + PushInterval: opts.Interval, + MaxQueueSize: opts.MaxQueueSize, + OnBeforeInsert: opts.OnBeforeInsert, + SvcNum: opts.ParallelNum, + AsyncInsert: opts.AsyncInsert, + InsertRequest: insertReq, + ServiceType: "time_series", + AcquireColumns: func() []service.IColPoolRes { + return (&TimeSeriesAcquirer{}).acq().serialize() + }, + ProcessRequest: func(ts any, res []service.IColPoolRes) (int, []service.IColPoolRes, error) { + timeSeriesData, ok := ts.(*model.TimeSeriesData) + if !ok { + logger.Info("invalid request type time series") + return 0, nil, fmt.Errorf("invalid request type time series") + } + acquirer := (&TimeSeriesAcquirer{}).deserialize(res) + _len := len(acquirer.Date.Data) + + for i, d := range timeSeriesData.MDate { + acquirer.Date.Data.Append(d) + acquirer.Labels.Data.Append(timeSeriesData.MLabels[i]) + } + + for _, Mf := range timeSeriesData.MFingerprint { + acquirer.Fingerprint.Data.Append(Mf) + } + + for _, MT := range timeSeriesData.MType { + acquirer.Type.Data.Append(MT) + } + return len(acquirer.Date.Data) - _len, acquirer.serialize(), nil + }, + } +} diff --git a/writer/service/insertColumnPools.go b/writer/service/insertColumnPools.go new file mode 100644 index 00000000..c14364f2 --- /dev/null +++ b/writer/service/insertColumnPools.go @@ -0,0 +1,209 @@ +package service + +import ( + "github.com/ClickHouse/ch-go/proto" + "github.com/metrico/qryn/writer/model" + "sync" +) + +func CreateColPools(size int32) { + DatePool = newColPool[proto.ColDate](func() proto.ColDate { + return make(proto.ColDate, 0, 10000) + }, size).OnRelease(func(col *PooledColumn[proto.ColDate]) { + col.Data = col.Data[:0] + }).OnGetSize(func(col *PooledColumn[proto.ColDate]) int { + return len(col.Data) + }) + Int64Pool = newColPool[proto.ColInt64](func() proto.ColInt64 { + return make(proto.ColInt64, 0, 10000) + }, size).OnRelease(func(col *PooledColumn[proto.ColInt64]) { + col.Data = col.Data[:0] + }).OnGetSize(func(col *PooledColumn[proto.ColInt64]) int { + return len(col.Data) + }) + + UInt64Pool = newColPool[proto.ColUInt64](func() proto.ColUInt64 { + return make(proto.ColUInt64, 0, 10000) + }, size).OnRelease(func(col *PooledColumn[proto.ColUInt64]) { + col.Data = col.Data[:0] + }).OnGetSize(func(col *PooledColumn[proto.ColUInt64]) int { + return len(col.Data) + }) + + UInt8Pool = newColPool[proto.ColUInt8](func() proto.ColUInt8 { + return make(proto.ColUInt8, 0, 1024*1024) + }, size).OnRelease(func(col *PooledColumn[proto.ColUInt8]) { + col.Data = col.Data[:0] + }).OnGetSize(func(col *PooledColumn[proto.ColUInt8]) int { + return col.Data.Rows() + }) + + UInt64ArrayPool = newColPool(func() *proto.ColArr[uint64] { + return proto.NewArray[uint64](&proto.ColUInt64{}) + }, size). + OnRelease(func(col *PooledColumn[*proto.ColArr[uint64]]) { + col.Data.Reset() + }). + OnGetSize(func(col *PooledColumn[*proto.ColArr[uint64]]) int { + return col.Data.Rows() + }) + + Uint32ColPool = newColPool[proto.ColUInt32](func() proto.ColUInt32 { + return make(proto.ColUInt32, 0, 10000) + }, size).OnRelease(func(col *PooledColumn[proto.ColUInt32]) { + col.Data = col.Data[:0] + }).OnGetSize(func(col *PooledColumn[proto.ColUInt32]) int { + return len(col.Data) + }) + + Float64Pool = newColPool[proto.ColFloat64](func() proto.ColFloat64 { + return make(proto.ColFloat64, 0, 10000) + }, size).OnRelease(func(col *PooledColumn[proto.ColFloat64]) { + col.Data = col.Data[:0] + }).OnGetSize(func(col *PooledColumn[proto.ColFloat64]) int { + return len(col.Data) + }) + StrPool = newColPool[*proto.ColStr](func() *proto.ColStr { + return &proto.ColStr{ + Buf: make([]byte, 0, 100000), + Pos: make([]proto.Position, 0, 10000), + } + }, size).OnRelease(func(col *PooledColumn[*proto.ColStr]) { + col.Data.Buf = col.Data.Buf[:0] + col.Data.Pos = col.Data.Pos[:0] + }).OnGetSize(func(col *PooledColumn[*proto.ColStr]) int { + return col.Data.Rows() + }) + FixedStringPool = newColPool[*proto.ColFixedStr](func() *proto.ColFixedStr { + return &proto.ColFixedStr{ + Buf: make([]byte, 0, 1024*1024), + Size: 8, + } + }, size).OnRelease(func(col *PooledColumn[*proto.ColFixedStr]) { + col.Data.Buf = col.Data.Buf[:0] + }).OnGetSize(func(col *PooledColumn[*proto.ColFixedStr]) int { + return col.Data.Rows() + }) + Int8ColPool = newColPool[proto.ColInt8](func() proto.ColInt8 { + return make(proto.ColInt8, 0, 1024*1024) + }, size).OnRelease(func(col *PooledColumn[proto.ColInt8]) { + col.Data = col.Data[:0] + }).OnGetSize(func(col *PooledColumn[proto.ColInt8]) int { + return col.Data.Rows() + }) + + BoolColPool = newColPool[proto.ColBool](func() proto.ColBool { + return make(proto.ColBool, 0, 1024*1024) + }, size).OnRelease(func(col *PooledColumn[proto.ColBool]) { + col.Data = col.Data[:0] + }).OnGetSize(func(col *PooledColumn[proto.ColBool]) int { + return col.Data.Rows() + }) + Uint16ColPool = newColPool[proto.ColUInt16](func() proto.ColUInt16 { + return make(proto.ColUInt16, 0, 1024*1024) + }, size).OnRelease(func(column *PooledColumn[proto.ColUInt16]) { + column.Data = column.Data[:0] + }).OnGetSize(func(column *PooledColumn[proto.ColUInt16]) int { + return column.Data.Rows() + }) + + TupleStrInt64Int32Pool = newColPool[*proto.ColArr[model.ValuesAgg]](func() *proto.ColArr[model.ValuesAgg] { + return proto.NewArray[model.ValuesAgg](ColTupleStrInt64Int32Adapter{proto.ColTuple{&proto.ColStr{}, &proto.ColInt64{}, &proto.ColInt32{}}}) + }, + size).OnRelease(func(col *PooledColumn[*proto.ColArr[model.ValuesAgg]]) { + col.Data.Reset() + }).OnGetSize(func(col *PooledColumn[*proto.ColArr[model.ValuesAgg]]) int { + return col.Data.Rows() + }) + + TupleUInt64StrPool = newColPool[*proto.ColArr[model.Function]](func() *proto.ColArr[model.Function] { + return proto.NewArray[model.Function](ColTupleFunctionAdapter{proto.ColTuple{&proto.ColUInt64{}, &proto.ColStr{}}}) + }, size).OnRelease(func(col *PooledColumn[*proto.ColArr[model.Function]]) { + col.Data.Reset() + }).OnGetSize(func(col *PooledColumn[*proto.ColArr[model.Function]]) int { + return col.Data.Rows() + }) + + TupleUInt64UInt64UInt64ArrPool = newColPool[*proto.ColArr[model.TreeRootStructure]](func() *proto.ColArr[model.TreeRootStructure] { + return proto.NewArray[model.TreeRootStructure](ColTupleTreeAdapter{ + proto.ColTuple{ + &proto.ColUInt64{}, + &proto.ColUInt64{}, + &proto.ColUInt64{}, + proto.NewArray[model.ValuesArrTuple](ColTupleTreeValueAdapter{proto.ColTuple{ + &proto.ColStr{}, + &proto.ColInt64{}, + &proto.ColInt64{}, + }}), + }, + }) + }, size).OnRelease(func(col *PooledColumn[*proto.ColArr[model.TreeRootStructure]]) { + col.Data.Reset() + }).OnGetSize(func(col *PooledColumn[*proto.ColArr[model.TreeRootStructure]]) int { + return col.Data.Rows() + }) + + TupleStrStrPool = newColPool[*proto.ColArr[model.StrStr]](func() *proto.ColArr[model.StrStr] { + return proto.NewArray[model.StrStr](ColTupleStrStrAdapter{proto.ColTuple{&proto.ColStr{}, &proto.ColStr{}}}) + // + //return proto.ColArr[proto.ColTuple]{} + }, size).OnRelease(func(col *PooledColumn[*proto.ColArr[model.StrStr]]) { + col.Data.Reset() + }).OnGetSize(func(col *PooledColumn[*proto.ColArr[model.StrStr]]) int { + return col.Data.Rows() + }) +} + +var DatePool *colPool[proto.ColDate] +var Int64Pool *colPool[proto.ColInt64] +var UInt64Pool *colPool[proto.ColUInt64] +var UInt8Pool *colPool[proto.ColUInt8] +var UInt64ArrayPool *colPool[*proto.ColArr[uint64]] +var Float64Pool *colPool[proto.ColFloat64] +var StrPool *colPool[*proto.ColStr] +var FixedStringPool *colPool[*proto.ColFixedStr] +var Int8ColPool *colPool[proto.ColInt8] +var BoolColPool *colPool[proto.ColBool] +var Uint16ColPool *colPool[proto.ColUInt16] +var TupleStrStrPool *colPool[*proto.ColArr[model.StrStr]] + +var TupleStrInt64Int32Pool *colPool[*proto.ColArr[model.ValuesAgg]] +var TupleUInt64UInt64UInt64ArrPool *colPool[*proto.ColArr[model.TreeRootStructure]] +var TupleUInt64StrPool *colPool[*proto.ColArr[model.Function]] +var Uint32ColPool *colPool[proto.ColUInt32] +var acqMtx sync.Mutex + +func acquire4Cols[T1, T2, T3, T4 proto.ColInput]( + p1 *colPool[T1], name1 string, + p2 *colPool[T2], name2 string, + p3 *colPool[T3], name3 string, + p4 *colPool[T4], name4 string) func() (*PooledColumn[T1], *PooledColumn[T2], + *PooledColumn[T3], *PooledColumn[T4]) { + return func() (*PooledColumn[T1], *PooledColumn[T2], *PooledColumn[T3], *PooledColumn[T4]) { + StartAcq() + defer FinishAcq() + return p1.Acquire(name1), p2.Acquire(name2), p3.Acquire(name3), p4.Acquire(name4) + } +} + +func acquire5Cols[T1, T2, T3, T4, T5 proto.ColInput]( + p1 *colPool[T1], name1 string, + p2 *colPool[T2], name2 string, + p3 *colPool[T3], name3 string, + p4 *colPool[T4], name4 string, + p5 *colPool[T5], name5 string) func() (*PooledColumn[T1], *PooledColumn[T2], *PooledColumn[T3], + *PooledColumn[T4], *PooledColumn[T5]) { + return func() (*PooledColumn[T1], *PooledColumn[T2], *PooledColumn[T3], *PooledColumn[T4], *PooledColumn[T5]) { + StartAcq() + defer FinishAcq() + return p1.Acquire(name1), p2.Acquire(name2), p3.Acquire(name3), p4.Acquire(name4), p5.Acquire(name5) + } +} + +func StartAcq() { + acqMtx.Lock() +} + +func FinishAcq() { + acqMtx.Unlock() +} diff --git a/writer/service/prevState_test.go b/writer/service/prevState_test.go new file mode 100644 index 00000000..f2ef2def --- /dev/null +++ b/writer/service/prevState_test.go @@ -0,0 +1,14 @@ +package service + +import ( + "fmt" + "testing" +) + +func TestArrayRevert(t *testing.T) { + a := []int{1} + b := a + a = append(a, 1) + a = b + fmt.Println(a) +} diff --git a/writer/service/registry/iServiceRegistry.go b/writer/service/registry/iServiceRegistry.go new file mode 100644 index 00000000..e36b2dcc --- /dev/null +++ b/writer/service/registry/iServiceRegistry.go @@ -0,0 +1,14 @@ +package registry + +import "github.com/metrico/qryn/writer/service" + +type IServiceRegistry interface { + GetTimeSeriesService(id string) (service.IInsertServiceV2, error) + GetSamplesService(id string) (service.IInsertServiceV2, error) + GetMetricsService(id string) (service.IInsertServiceV2, error) + GetSpansService(id string) (service.IInsertServiceV2, error) + GetSpansSeriesService(id string) (service.IInsertServiceV2, error) + GetProfileInsertService(id string) (service.IInsertServiceV2, error) + Run() + Stop() +} diff --git a/writer/service/registry/staticServiceRegistry.go b/writer/service/registry/staticServiceRegistry.go new file mode 100644 index 00000000..6973df5c --- /dev/null +++ b/writer/service/registry/staticServiceRegistry.go @@ -0,0 +1,101 @@ +package registry + +import ( + "github.com/metrico/qryn/writer/service" + "math/rand" + "sync" + "time" +) + +type staticServiceRegistry struct { + TimeSeriesSvcs []service.IInsertServiceV2 + SamplesSvcs []service.IInsertServiceV2 + MetricSvcs []service.IInsertServiceV2 + TempoSamplesSvc []service.IInsertServiceV2 + TempoTagsSvc []service.IInsertServiceV2 + ProfileInsertSvc []service.IInsertServiceV2 + rand *rand.Rand + mtx sync.Mutex +} + +func NewStaticServiceRegistry( + TimeSeriesSvcs map[string]service.IInsertServiceV2, + SamplesSvcs map[string]service.IInsertServiceV2, + MetricSvcs map[string]service.IInsertServiceV2, + TempoSamplesSvc map[string]service.IInsertServiceV2, + TempoTagsSvc map[string]service.IInsertServiceV2, + ProfileInsertSvc map[string]service.IInsertServiceV2) IServiceRegistry { + res := staticServiceRegistry{ + rand: rand.New(rand.NewSource(time.Now().UnixNano())), + } + for _, s := range TimeSeriesSvcs { + res.TimeSeriesSvcs = append(res.TimeSeriesSvcs, s) + } + for _, s := range SamplesSvcs { + res.SamplesSvcs = append(res.SamplesSvcs, s) + } + for _, s := range MetricSvcs { + res.MetricSvcs = append(res.MetricSvcs, s) + } + for _, s := range TempoSamplesSvc { + res.TempoSamplesSvc = append(res.TempoSamplesSvc, s) + } + + for _, s := range ProfileInsertSvc { + res.ProfileInsertSvc = append(res.ProfileInsertSvc, s) + } + for _, s := range TempoTagsSvc { + res.TempoTagsSvc = append(res.TempoTagsSvc, s) + } + return &res +} + +func staticServiceRegistryGetService[T interface{ GetNodeName() string }](r *staticServiceRegistry, id string, + svcs []T) (T, error) { + if id != "" { + for _, svc := range svcs { + if svc.GetNodeName() == id { + return svc, nil + } + } + } + r.mtx.Lock() + defer r.mtx.Unlock() + idx := r.rand.Intn(len(svcs)) + return svcs[idx], nil +} + +func (r *staticServiceRegistry) getService(id string, + svcs []service.IInsertServiceV2) (service.IInsertServiceV2, error) { + return staticServiceRegistryGetService(r, id, svcs) +} + +func (r *staticServiceRegistry) GetTimeSeriesService(id string) (service.IInsertServiceV2, error) { + return r.getService(id, r.TimeSeriesSvcs) +} + +func (r *staticServiceRegistry) GetSamplesService(id string) (service.IInsertServiceV2, error) { + return r.getService(id, r.SamplesSvcs) + +} + +func (r *staticServiceRegistry) GetMetricsService(id string) (service.IInsertServiceV2, error) { + return r.getService(id, r.MetricSvcs) + +} + +func (r *staticServiceRegistry) GetSpansService(id string) (service.IInsertServiceV2, error) { + return r.getService(id, r.TempoSamplesSvc) + +} + +func (r *staticServiceRegistry) GetSpansSeriesService(id string) (service.IInsertServiceV2, error) { + return r.getService(id, r.TempoTagsSvc) +} + +func (r *staticServiceRegistry) GetProfileInsertService(id string) (service.IInsertServiceV2, error) { + return r.getService(id, r.ProfileInsertSvc) +} +func (r *staticServiceRegistry) Run() {} + +func (r *staticServiceRegistry) Stop() {} diff --git a/writer/service/serviceService.go b/writer/service/serviceService.go new file mode 100644 index 00000000..8045d90c --- /dev/null +++ b/writer/service/serviceService.go @@ -0,0 +1,14 @@ +package service + +// Service : here you tell us what Salutation is + +type ServiceData struct { +} + +// ServiceConfig +type ServiceConfig struct { +} + +// ServiceConfigDatabases +type ServiceConfigDatabases struct { +} diff --git a/writer/setup_check.go b/writer/setup_check.go new file mode 100644 index 00000000..d0b1e732 --- /dev/null +++ b/writer/setup_check.go @@ -0,0 +1,85 @@ +package writer + +import ( + "context" + "fmt" + "github.com/metrico/qryn/writer/ch_wrapper" + "github.com/metrico/qryn/writer/config" + "github.com/metrico/qryn/writer/utils/logger" + "runtime" + "strconv" + "time" +) + +type SetupState struct { + Version string + Type string + Shards int + SamplesChannels int + TSChannels int + Preforking bool + Forks int +} + +func (s SetupState) ToLogLines() []string { + shards := strconv.FormatInt(int64(s.Shards), 10) + if s.Shards == 0 { + shards = "can't retrieve" + } + return []string{ + "QRYN-WRITER SETTINGS:", + "qryn-writer version: " + s.Version, + "clickhouse setup type: " + s.Type, + "shards: " + shards, + "samples channels: " + strconv.FormatInt(int64(s.SamplesChannels), 10), + "time-series channels: " + strconv.FormatInt(int64(s.TSChannels), 10), + fmt.Sprintf("preforking: %v", s.Preforking), + "forks: " + strconv.FormatInt(int64(s.Forks), 10), + } +} + +func checkSetup(conn ch_wrapper.IChClient) SetupState { + setupType := "single-server" + if config.Cloki.Setting.DATABASE_DATA[0].ClusterName != "" && config.Cloki.Setting.DATABASE_DATA[0].Cloud { + setupType = "Distributed + Replicated" + } else if config.Cloki.Setting.DATABASE_DATA[0].ClusterName != "" { + setupType = "Distributed" + } else if config.Cloki.Setting.DATABASE_DATA[0].Cloud { + setupType = "Replicated" + } + shards := 1 + if config.Cloki.Setting.DATABASE_DATA[0].ClusterName != "" { + shards = getShardsNum(conn, config.Cloki.Setting.DATABASE_DATA[0].ClusterName) + } + forks := 1 + if config.Cloki.Setting.HTTP_SETTINGS.Prefork { + forks = runtime.GOMAXPROCS(0) + } + return SetupState{ + Version: "", + Type: setupType, + Shards: shards, + SamplesChannels: config.Cloki.Setting.SYSTEM_SETTINGS.ChannelsSample, + TSChannels: config.Cloki.Setting.SYSTEM_SETTINGS.ChannelsTimeSeries, + Preforking: config.Cloki.Setting.HTTP_SETTINGS.Prefork, + Forks: forks, + } +} + +func getShardsNum(conn ch_wrapper.IChClient, clusterName string) int { + to, _ := context.WithTimeout(context.Background(), time.Second*30) + rows, err := conn.Query(to, "select count(distinct shard_num) from system.clusters where cluster=$1", clusterName) + if err != nil { + logger.Error("[GSN001] Get shards error: ", err) + return 0 + } + defer rows.Close() + var res uint64 + rows.Next() + err = rows.Scan(&res) + if err != nil { + logger.Error("[GSN002] Get shards error: ", err) + return 0 + } + return int(res) +} diff --git a/writer/utils/errors/error.go b/writer/utils/errors/error.go new file mode 100644 index 00000000..8e6743a9 --- /dev/null +++ b/writer/utils/errors/error.go @@ -0,0 +1,85 @@ +package custom_errors + +import ( + "errors" +) + +// Define base error values for comparison +var ( + ErrNotImplemented = &QrynError{501, "not implemented"} + ErrNotFound = &QrynError{404, "not implemented"} +) + +// IQrynError interface +type IQrynError interface { + error + IsQrynError() bool + GetCode() int +} + +type UnMarshalError struct { + Message string + Code int +} + +func (u *UnMarshalError) GetCode() int { + return u.Code +} + +func (u *UnMarshalError) IsQrynError() bool { + return true +} + +func (u *UnMarshalError) Error() string { + return u.Message +} + +// QrynError struct implementing IQrynError +type QrynError struct { + Code int + Message string +} + +func (e *QrynError) Error() string { + return e.Message +} + +func (e *QrynError) IsQrynError() bool { + return true +} + +func (e *QrynError) GetCode() int { + return e.Code +} + +func New400Error(msg string) IQrynError { + return &QrynError{Code: 400, Message: msg} +} + +func New401Error(msg string) IQrynError { + return &QrynError{Code: 401, Message: msg} +} + +func New429Error(msg string) IQrynError { + return &QrynError{Code: 429, Message: msg} +} + +// NewUnmarshalError creates a new instance of UnmarshalError. +func NewUnmarshalError(err error) IQrynError { + var target IQrynError + if errors.As(err, &target) { + return target + } + return &UnMarshalError{ + err.Error(), + 400, + } +} + +func Unwrap[T IQrynError](err error) (T, bool) { + var target T + if errors.As(err, &target) { + return target, true + } + return target, false +} diff --git a/writer/utils/fingerprint.go b/writer/utils/fingerprint.go new file mode 100755 index 00000000..8c723b22 --- /dev/null +++ b/writer/utils/fingerprint.go @@ -0,0 +1,41 @@ +package heputils + +var ( + offset64 uint64 = 14695981039346656037 + prime64 uint64 = 14695981039346656037 + separatorByte byte = 255 +) + +// hashAdd adds a string to a fnv64a hash value, returning the updated hash. +func hashAdd(h uint64, s string) uint64 { + for i := 0; i < len(s); i++ { + h ^= uint64(s[i]) + h *= prime64 + } + return h +} + +// hashAddByte adds a byte to a fnv64a hash value, returning the updated hash. +func hashAddByte(h uint64, b byte) uint64 { + h ^= uint64(b) + h *= prime64 + return h +} + +// Fingerprint calculates a fingerprint of SORTED BY NAME labels. +// It is adopted from labelSetToFingerprint, but avoids type conversions and memory allocations. +func FingerprintLabels(labels [][]string) uint64 { + + if len(labels) == 0 { + return offset64 + } + + sum := offset64 + for _, v := range labels { + sum = hashAdd(sum, v[0]) + sum = hashAddByte(sum, separatorByte) + sum = hashAdd(sum, v[1]) + sum = hashAddByte(sum, separatorByte) + } + return sum +} diff --git a/writer/utils/helpers/limitedBuffer.go b/writer/utils/helpers/limitedBuffer.go new file mode 100644 index 00000000..6da7d551 --- /dev/null +++ b/writer/utils/helpers/limitedBuffer.go @@ -0,0 +1,218 @@ +package helpers + +import ( + "bytes" + "compress/gzip" + "context" + "fmt" + "github.com/gofiber/fiber/v2" + "github.com/golang/snappy" + custom_errors "github.com/metrico/qryn/writer/utils/errors" + "github.com/metrico/qryn/writer/utils/logger" + "github.com/valyala/bytebufferpool" + "golang.org/x/sync/semaphore" + "io" + "strconv" + "time" +) + +var RateLimitingEnable = false + +type UUID [16]byte +type RateLimitedBuffer interface { + Bytes() []byte + Write([]byte) (int, error) + Release() +} + +type RateLimitedPooledBuffer struct { + pool *RateLimitedPool + limit int + buffer *bytebufferpool.ByteBuffer +} + +func (l *RateLimitedPooledBuffer) Write(msg []byte) (int, error) { + if len(msg)+l.buffer.Len() > l.limit { + return 0, custom_errors.New400Error("buffer size overflow") + } + return l.buffer.Write(msg) +} + +func (l *RateLimitedPooledBuffer) Bytes() []byte { + return l.buffer.Bytes() +} + +func (l *RateLimitedPooledBuffer) Release() { + l.pool.releasePooledBuffer(l) +} + +type RateLimitedSliceBuffer struct { + bytes []byte + pool *RateLimitedPool +} + +func (l *RateLimitedSliceBuffer) Write(msg []byte) (int, error) { + return 0, nil +} + +func (l *RateLimitedSliceBuffer) Bytes() []byte { + return l.bytes +} + +func (l *RateLimitedSliceBuffer) Release() { + if l.pool != nil { + l.pool.releaseSlice(l) + } +} + +type RateLimitedPool struct { + limit int + rateLimiter *semaphore.Weighted +} + +func (r *RateLimitedPool) acquirePooledBuffer(limit int) (RateLimitedBuffer, error) { + if limit > r.limit { + return nil, fmt.Errorf("limit too big") + } + if RateLimitingEnable { + to, _ := context.WithTimeout(context.Background(), time.Second) + err := r.rateLimiter.Acquire(to, int64(limit)) + if err != nil { + return nil, err + } + } + return &RateLimitedPooledBuffer{ + pool: r, + limit: limit, + buffer: bytebufferpool.Get(), + }, nil +} + +func (r *RateLimitedPool) releasePooledBuffer(buffer *RateLimitedPooledBuffer) { + if RateLimitingEnable { + r.rateLimiter.Release(int64(buffer.limit)) + } + //bytebufferpool.Put(buffer.buffer) +} + +func (r *RateLimitedPool) acquireSlice(size int) (RateLimitedBuffer, error) { + if size > r.limit { + return nil, custom_errors.New400Error("size too big") + } + if RateLimitingEnable { + to, _ := context.WithTimeout(context.Background(), time.Second) + err := r.rateLimiter.Acquire(to, int64(size)) + if err != nil { + return nil, err + } + } + return &RateLimitedSliceBuffer{ + bytes: make([]byte, size), + pool: r, + }, nil +} + +func (r *RateLimitedPool) releaseSlice(buffer *RateLimitedSliceBuffer) { + //r.rateLimiter.Release(int64(len(buffer.bytes))) +} + +var requestPool = RateLimitedPool{ + limit: 50 * 1024 * 1024, + rateLimiter: semaphore.NewWeighted(50 * 1024 * 1024), +} +var pbPool = RateLimitedPool{ + limit: 50 * 1024 * 1024, + rateLimiter: semaphore.NewWeighted(50 * 1024 * 1024), +} + +func getPayloadBuffer(ctx *fiber.Ctx) (RateLimitedBuffer, error) { + var ctxLen int + if ctx.Get("content-length", "") == "" { + return nil, custom_errors.New400Error("content-length is required") + //return nil, util.NewCLokiWriterError(400, "content-length is required") + } else { + ctxLen, _ = strconv.Atoi(ctx.Get("content-length", "")) + } + buf, err := requestPool.acquirePooledBuffer(ctxLen) + if err != nil { + return nil, err + } + _, err = io.Copy(buf, ctx.Context().RequestBodyStream()) + if err != nil { + buf.Release() + return nil, err + } + return buf, nil +} + +func decompressPayload(buf RateLimitedBuffer) (RateLimitedBuffer, error) { + decompSize, err := snappy.DecodedLen(buf.Bytes()) + if err != nil { + return nil, err + } + if decompSize > pbPool.limit { + return nil, custom_errors.New400Error("decompressed request too long") + //return nil, util.NewCLokiWriterError(400, "decompressed request too long") + } + slice, err := pbPool.acquireSlice(decompSize) + if err != nil { + return nil, err + } + _, err = snappy.Decode(slice.Bytes(), buf.Bytes()) + if err != nil { + slice.Release() + logger.Error(err) + return nil, custom_errors.New400Error("request decompress error") + //return nil, util.NewCLokiWriterError(400, "request decompress error") + } + return slice, nil +} + +func GetRawBody(ctx *fiber.Ctx) (RateLimitedBuffer, error) { + buf, err := getPayloadBuffer(ctx) + if err != nil { + return nil, err + } + if ctx.Get("content-encoding") == "gzip" { + defer buf.Release() + reader := bytes.NewReader([]byte(buf.Bytes())) + gzreader, err := gzip.NewReader(reader) + if err != nil { + return nil, err + } + buf2 := bytes.Buffer{} + _, err = io.Copy(&buf2, gzreader) + if err != nil { + return nil, err + } + return &RateLimitedSliceBuffer{bytes: buf2.Bytes()}, nil + } + if ctx.Get("content-type", "") != "application/x-protobuf" { + return buf, nil + } + defer buf.Release() + //t1 := time.Now().UnixNano() + slice, err := decompressPayload(buf) + //stat.AddSentMetrics("Decompression time", time.Now().UnixNano()-t1) + return slice, err +} + +func GetRawCompressedBody(ctx *fiber.Ctx) (RateLimitedBuffer, error) { + return getPayloadBuffer(ctx) +} + +func GetRawPB(ctx *fiber.Ctx) (RateLimitedBuffer, error) { + buf, err := getPayloadBuffer(ctx) + if err != nil { + return nil, err + } + defer buf.Release() + return decompressPayload(buf) +} + +func SetGlobalLimit(limit int) { + requestPool.limit = limit / 2 + requestPool.rateLimiter = semaphore.NewWeighted(int64(limit / 2)) + pbPool.limit = limit / 2 + pbPool.rateLimiter = semaphore.NewWeighted(int64(limit / 2)) +} diff --git a/writer/utils/helpers/sizeGetter.go b/writer/utils/helpers/sizeGetter.go new file mode 100644 index 00000000..2bb3c702 --- /dev/null +++ b/writer/utils/helpers/sizeGetter.go @@ -0,0 +1,5 @@ +package helpers + +type SizeGetter interface { + GetSize() int64 +} diff --git a/writer/utils/heputils/cityhash102/cityhash.go b/writer/utils/heputils/cityhash102/cityhash.go new file mode 100644 index 00000000..5d161b20 --- /dev/null +++ b/writer/utils/heputils/cityhash102/cityhash.go @@ -0,0 +1,383 @@ +/* + * Go implementation of Google city hash (MIT license) + * https://code.google.com/p/cityhash/ + * + * MIT License http://www.opensource.org/licenses/mit-license.php + * + * I don't even want to pretend to understand the details of city hash. + * I am only reproducing the logic in Go as faithfully as I can. + * + */ + +package cityhash102 + +import ( + "encoding/binary" +) + +const ( + k0 uint64 = 0xc3a5c85c97cb3127 + k1 uint64 = 0xb492b66fbe98f273 + k2 uint64 = 0x9ae16a3b2f90404f + k3 uint64 = 0xc949d7c7509e6557 + + kMul uint64 = 0x9ddfea08eb382d69 +) + +func fetch64(p []byte) uint64 { + return binary.LittleEndian.Uint64(p) + //return uint64InExpectedOrder(unalignedLoad64(p)) +} + +func fetch32(p []byte) uint32 { + return binary.LittleEndian.Uint32(p) + //return uint32InExpectedOrder(unalignedLoad32(p)) +} + +func rotate64(val uint64, shift uint32) uint64 { + if shift != 0 { + return ((val >> shift) | (val << (64 - shift))) + } + + return val +} + +func rotate32(val uint32, shift uint32) uint32 { + if shift != 0 { + return ((val >> shift) | (val << (32 - shift))) + } + + return val +} + +func swap64(a, b *uint64) { + *a, *b = *b, *a +} + +func swap32(a, b *uint32) { + *a, *b = *b, *a +} + +func permute3(a, b, c *uint32) { + swap32(a, b) + swap32(a, c) +} + +func rotate64ByAtLeast1(val uint64, shift uint32) uint64 { + return (val >> shift) | (val << (64 - shift)) +} + +func shiftMix(val uint64) uint64 { + return val ^ (val >> 47) +} + +type Uint128 [2]uint64 + +func (this *Uint128) setLower64(l uint64) { + this[0] = l +} + +func (this *Uint128) setHigher64(h uint64) { + this[1] = h +} + +func (this Uint128) Lower64() uint64 { + return this[0] +} + +func (this Uint128) Higher64() uint64 { + return this[1] +} + +func (this Uint128) Bytes() []byte { + b := make([]byte, 16) + binary.LittleEndian.PutUint64(b, this[0]) + binary.LittleEndian.PutUint64(b[8:], this[1]) + return b +} + +func Hash128to64(x Uint128) uint64 { + // Murmur-inspired hashing. + var a = (x.Lower64() ^ x.Higher64()) * kMul + a ^= (a >> 47) + var b = (x.Higher64() ^ a) * kMul + b ^= (b >> 47) + b *= kMul + return b +} + +func hashLen16(u, v uint64) uint64 { + return Hash128to64(Uint128{u, v}) +} + +func hashLen16_3(u, v, mul uint64) uint64 { + // Murmur-inspired hashing. + var a = (u ^ v) * mul + a ^= (a >> 47) + var b = (v ^ a) * mul + b ^= (b >> 47) + b *= mul + return b +} + +func hashLen0to16(s []byte, length uint32) uint64 { + if length > 8 { + var a = fetch64(s) + var b = fetch64(s[length-8:]) + + return hashLen16(a, rotate64ByAtLeast1(b+uint64(length), length)) ^ b + } + + if length >= 4 { + var a = fetch32(s) + return hashLen16(uint64(length)+(uint64(a)<<3), uint64(fetch32(s[length-4:]))) + } + + if length > 0 { + var a uint8 = uint8(s[0]) + var b uint8 = uint8(s[length>>1]) + var c uint8 = uint8(s[length-1]) + + var y uint32 = uint32(a) + (uint32(b) << 8) + var z uint32 = length + (uint32(c) << 2) + + return shiftMix(uint64(y)*k2^uint64(z)*k3) * k2 + } + + return k2 +} + +// This probably works well for 16-byte strings as well, but it may be overkill +func hashLen17to32(s []byte, length uint32) uint64 { + var a = fetch64(s) * k1 + var b = fetch64(s[8:]) + var c = fetch64(s[length-8:]) * k2 + var d = fetch64(s[length-16:]) * k0 + + return hashLen16(rotate64(a-b, 43)+rotate64(c, 30)+d, + a+rotate64(b^k3, 20)-c+uint64(length)) +} + +func weakHashLen32WithSeeds(w, x, y, z, a, b uint64) Uint128 { + a += w + b = rotate64(b+a+z, 21) + var c uint64 = a + a += x + a += y + b += rotate64(a, 44) + return Uint128{a + z, b + c} +} + +func weakHashLen32WithSeeds_3(s []byte, a, b uint64) Uint128 { + return weakHashLen32WithSeeds(fetch64(s), fetch64(s[8:]), fetch64(s[16:]), fetch64(s[24:]), a, b) +} + +func hashLen33to64(s []byte, length uint32) uint64 { + var z uint64 = fetch64(s[24:]) + var a uint64 = fetch64(s) + (uint64(length)+fetch64(s[length-16:]))*k0 + var b uint64 = rotate64(a+z, 52) + var c uint64 = rotate64(a, 37) + + a += fetch64(s[8:]) + c += rotate64(a, 7) + a += fetch64(s[16:]) + + var vf uint64 = a + z + var vs = b + rotate64(a, 31) + c + + a = fetch64(s[16:]) + fetch64(s[length-32:]) + z = fetch64(s[length-8:]) + b = rotate64(a+z, 52) + c = rotate64(a, 37) + a += fetch64(s[length-24:]) + c += rotate64(a, 7) + a += fetch64(s[length-16:]) + + wf := a + z + ws := b + rotate64(a, 31) + c + r := shiftMix((vf+ws)*k2 + (wf+vs)*k0) + return shiftMix(r*k0+vs) * k2 +} + +func CityHash64(s []byte, length uint32) uint64 { + if length <= 32 { + if length <= 16 { + return hashLen0to16(s, length) + } else { + return hashLen17to32(s, length) + } + } else if length <= 64 { + return hashLen33to64(s, length) + } + + var x uint64 = fetch64(s) + var y uint64 = fetch64(s[length-16:]) ^ k1 + var z uint64 = fetch64(s[length-56:]) ^ k0 + + var v Uint128 = weakHashLen32WithSeeds_3(s[length-64:], uint64(length), y) + var w Uint128 = weakHashLen32WithSeeds_3(s[length-32:], uint64(length)*k1, k0) + + z += shiftMix(v.Higher64()) * k1 + x = rotate64(z+x, 39) * k1 + y = rotate64(y, 33) * k1 + + length = (length - 1) & ^uint32(63) + for { + x = rotate64(x+y+v.Lower64()+fetch64(s[16:]), 37) * k1 + y = rotate64(y+v.Higher64()+fetch64(s[48:]), 42) * k1 + + x ^= w.Higher64() + y ^= v.Lower64() + + z = rotate64(z^w.Lower64(), 33) + v = weakHashLen32WithSeeds_3(s, v.Higher64()*k1, x+w.Lower64()) + w = weakHashLen32WithSeeds_3(s[32:], z+w.Higher64(), y) + + swap64(&z, &x) + s = s[64:] + length -= 64 + + if length == 0 { + break + } + } + + return hashLen16(hashLen16(v.Lower64(), w.Lower64())+shiftMix(y)*k1+z, hashLen16(v.Higher64(), w.Higher64())+x) +} + +func CityHash64WithSeed(s []byte, length uint32, seed uint64) uint64 { + return CityHash64WithSeeds(s, length, k2, seed) +} + +func CityHash64WithSeeds(s []byte, length uint32, seed0, seed1 uint64) uint64 { + return hashLen16(CityHash64(s, length)-seed0, seed1) +} + +func cityMurmur(s []byte, length uint32, seed Uint128) Uint128 { + var a uint64 = seed.Lower64() + var b uint64 = seed.Higher64() + var c uint64 = 0 + var d uint64 = 0 + var l int32 = int32(length) - 16 + + if l <= 0 { // len <= 16 + a = shiftMix(a*k1) * k1 + c = b*k1 + hashLen0to16(s, length) + + if length >= 8 { + d = shiftMix(a + fetch64(s)) + } else { + d = shiftMix(a + c) + } + + } else { // len > 16 + c = hashLen16(fetch64(s[length-8:])+k1, a) + d = hashLen16(b+uint64(length), c+fetch64(s[length-16:])) + a += d + + for { + a ^= shiftMix(fetch64(s)*k1) * k1 + a *= k1 + b ^= a + c ^= shiftMix(fetch64(s[8:])*k1) * k1 + c *= k1 + d ^= c + s = s[16:] + l -= 16 + + if l <= 0 { + break + } + } + } + a = hashLen16(a, c) + b = hashLen16(d, b) + return Uint128{a ^ b, hashLen16(b, a)} +} + +func CityHash128WithSeed(s []byte, length uint32, seed Uint128) Uint128 { + if length < 128 { + return cityMurmur(s, length, seed) + } + + // We expect length >= 128 to be the common case. Keep 56 bytes of state: + // v, w, x, y, and z. + var v, w Uint128 + var x uint64 = seed.Lower64() + var y uint64 = seed.Higher64() + var z uint64 = uint64(length) * k1 + + var pos uint32 + var t = s + + v.setLower64(rotate64(y^k1, 49)*k1 + fetch64(s)) + v.setHigher64(rotate64(v.Lower64(), 42)*k1 + fetch64(s[8:])) + w.setLower64(rotate64(y+z, 35)*k1 + x) + w.setHigher64(rotate64(x+fetch64(s[88:]), 53) * k1) + + // This is the same inner loop as CityHash64(), manually unrolled. + for { + x = rotate64(x+y+v.Lower64()+fetch64(s[16:]), 37) * k1 + y = rotate64(y+v.Higher64()+fetch64(s[48:]), 42) * k1 + + x ^= w.Higher64() + y ^= v.Lower64() + z = rotate64(z^w.Lower64(), 33) + v = weakHashLen32WithSeeds_3(s, v.Higher64()*k1, x+w.Lower64()) + w = weakHashLen32WithSeeds_3(s[32:], z+w.Higher64(), y) + swap64(&z, &x) + s = s[64:] + pos += 64 + + x = rotate64(x+y+v.Lower64()+fetch64(s[16:]), 37) * k1 + y = rotate64(y+v.Higher64()+fetch64(s[48:]), 42) * k1 + x ^= w.Higher64() + y ^= v.Lower64() + z = rotate64(z^w.Lower64(), 33) + v = weakHashLen32WithSeeds_3(s, v.Higher64()*k1, x+w.Lower64()) + w = weakHashLen32WithSeeds_3(s[32:], z+w.Higher64(), y) + swap64(&z, &x) + s = s[64:] + pos += 64 + length -= 128 + + if length < 128 { + break + } + } + + y += rotate64(w.Lower64(), 37)*k0 + z + x += rotate64(v.Lower64()+z, 49) * k0 + + // If 0 < length < 128, hash up to 4 chunks of 32 bytes each from the end of s. + var tailDone uint32 + for tailDone = 0; tailDone < length; { + tailDone += 32 + y = rotate64(y-x, 42)*k0 + v.Higher64() + + //TODO why not use origin_len ? + w.setLower64(w.Lower64() + fetch64(t[pos+length-tailDone+16:])) + x = rotate64(x, 49)*k0 + w.Lower64() + w.setLower64(w.Lower64() + v.Lower64()) + v = weakHashLen32WithSeeds_3(t[pos+length-tailDone:], v.Lower64(), v.Higher64()) + } + // At this point our 48 bytes of state should contain more than + // enough information for a strong 128-bit hash. We use two + // different 48-byte-to-8-byte hashes to get a 16-byte final result. + x = hashLen16(x, v.Lower64()) + y = hashLen16(y, w.Lower64()) + + return Uint128{hashLen16(x+v.Higher64(), w.Higher64()) + y, + hashLen16(x+w.Higher64(), y+v.Higher64())} +} + +func CityHash128(s []byte, length uint32) (result Uint128) { + if length >= 16 { + result = CityHash128WithSeed(s[16:length], length-16, Uint128{fetch64(s) ^ k3, fetch64(s[8:])}) + } else if length >= 8 { + result = CityHash128WithSeed(nil, 0, Uint128{fetch64(s) ^ (uint64(length) * k0), fetch64(s[length-8:]) ^ k1}) + } else { + result = CityHash128WithSeed(s, length, Uint128{k0, k1}) + } + return +} diff --git a/writer/utils/heputils/fingerprint.go b/writer/utils/heputils/fingerprint.go new file mode 100755 index 00000000..ebac18eb --- /dev/null +++ b/writer/utils/heputils/fingerprint.go @@ -0,0 +1,18 @@ +package heputils + +type UUID [16]byte + +// Javascript port +func FingerprintLabelsDJBHashPrometheus(data []byte) uint32 { + + if data == nil { + return 0 + } + + var hash int32 = 5381 + + for i := len(data) - 1; i > -1; i-- { + hash = (hash * 33) ^ int32(uint16(data[i])) + } + return uint32(hash) +} diff --git a/writer/utils/httpresponse/httpresponse.go b/writer/utils/httpresponse/httpresponse.go new file mode 100644 index 00000000..c41b8f72 --- /dev/null +++ b/writer/utils/httpresponse/httpresponse.go @@ -0,0 +1,49 @@ +package httpresponse + +import ( + "github.com/gofiber/fiber/v2" +) + +func CreateBadResponse(ctx *fiber.Ctx, requestCode int, message string) error { + + return ctx.Status(requestCode).JSON(&fiber.Map{ + "success": false, + "message": message, + }) +} + +func CreateBadResponseWithCode(ctx *fiber.Ctx, requestCode int, errorCode int, message string) error { + + return ctx.Status(requestCode).JSON(&fiber.Map{ + "success": false, + "errorcode": errorCode, + "message": message, + }) +} + +func CreateSuccessResponse(ctx *fiber.Ctx, requestCode int, message string) error { + + return ctx.Status(requestCode).JSON(&fiber.Map{ + "success": true, + "message": message, + }) +} + +func CreateBadResponseWithJson(ctx *fiber.Ctx, requestCode int, message []byte) error { + + return ctx.Status(requestCode).JSON(&fiber.Map{ + "success": false, + "message": message, + }) +} + +func CreateSuccessResponseWithJson(ctx *fiber.Ctx, requestCode int, message []byte) error { + return ctx.Status(requestCode).JSON(&fiber.Map{ + "success": true, + "message": message, + }) +} + +func CreateSuccessResponseWTBody(ctx *fiber.Ctx, requestCode int) error { + return ctx.Status(requestCode).SendString("") +} diff --git a/writer/utils/logger/logger.go b/writer/utils/logger/logger.go new file mode 100644 index 00000000..4fde825b --- /dev/null +++ b/writer/utils/logger/logger.go @@ -0,0 +1,314 @@ +package logger + +import ( + "bytes" + "encoding/json" + "fmt" + "log" + "log/syslog" + "net/http" + "os" + "path/filepath" + "strconv" + "strings" + "sync" + "time" + + "github.com/metrico/qryn/writer/config" + + rotatelogs "github.com/lestrrat-go/file-rotatelogs" + "github.com/sirupsen/logrus" +) + +const ( + SYSLOG_LOG_EMERG = "LOG_EMERG" + SYSLOG_LOG_ALERT = "LOG_ALERT" + SYSLOG_LOG_CRIT = "LOG_CRIT" + SYSLOG_LOG_ERR = "LOG_ERR" + SYSLOG_LOG_WARNING = "LOG_WARNING" + SYSLOG_LOG_NOTICE = "LOG_NOTICE" + SYSLOG_LOG_INFO = "LOG_INFO" + SYSLOG_LOG_DEBUG = "LOG_DEBUG" +) + +type LogInfo logrus.Fields + +var RLogs *rotatelogs.RotateLogs +var Logger = logrus.New() + +type DbLogger struct{} + +/* db logger for logrus */ +func (*DbLogger) Print(v ...interface{}) { + if v[0] == "sql" { + Logger.WithFields(logrus.Fields{"module": "db", "type": "sql"}).Print(v[3]) + } + if v[0] == "log" { + Logger.WithFields(logrus.Fields{"module": "db", "type": "log"}).Print(v[2]) + } +} + +// initLogger function +func InitLogger() { + + //env := os.Getenv("environment") + //isLocalHost := env == "local" + if config.Cloki.Setting.LOG_SETTINGS.Json { + // Log as JSON instead of the default ASCII formatter. + Logger.SetFormatter(&logrus.JSONFormatter{}) + } else { + Logger.Formatter.(*logrus.TextFormatter).DisableTimestamp = false + Logger.Formatter.(*logrus.TextFormatter).DisableColors = true + } + if config.Cloki.Setting.LOG_SETTINGS.Qryn.Url != "" { + hostname := "" + if config.Cloki.Setting.LOG_SETTINGS.Qryn.AddHostname { + hostname, _ = os.Hostname() + } + + headers := map[string]string{} + for _, h := range strings.Split(config.Cloki.Setting.LOG_SETTINGS.Qryn.Headers, ";;") { + pair := strings.Split(h, ":") + headers[pair[0]] = strings.Join(pair[1:], ":") + } + + qrynFmt := &qrynFormatter{ + formatter: Logger.Formatter, + url: config.Cloki.Setting.LOG_SETTINGS.Qryn.Url, + app: config.Cloki.Setting.LOG_SETTINGS.Qryn.App, + hostname: hostname, + headers: headers, + } + Logger.SetFormatter(qrynFmt) + qrynFmt.Run() + } + // Output to stdout instead of the default stderr + // Can be any io.Writer, see below for File example + if config.Cloki.Setting.LOG_SETTINGS.Stdout { + Logger.SetOutput(os.Stdout) + log.SetOutput(os.Stdout) + } + + /* log level default */ + if config.Cloki.Setting.LOG_SETTINGS.Level == "" { + config.Cloki.Setting.LOG_SETTINGS.Level = "info" + } + + if logLevel, ok := logrus.ParseLevel(config.Cloki.Setting.LOG_SETTINGS.Level); ok == nil { + // Only log the warning severity or above. + Logger.SetLevel(logLevel) + } else { + Logger.Error("Couldn't parse loglevel", config.Cloki.Setting.LOG_SETTINGS.Level) + Logger.SetLevel(logrus.ErrorLevel) + } + + Logger.Info("init logging system") + + if !config.Cloki.Setting.LOG_SETTINGS.Stdout && !config.Cloki.Setting.LOG_SETTINGS.SysLog { + // configure file system hook + configureLocalFileSystemHook() + } else if !config.Cloki.Setting.LOG_SETTINGS.Stdout { + configureSyslogHook() + } +} + +// SetLoggerLevel function +func SetLoggerLevel(loglevelString string) { + + if logLevel, ok := logrus.ParseLevel(loglevelString); ok == nil { + // Only log the warning severity or above. + Logger.SetLevel(logLevel) + } else { + Logger.Error("Couldn't parse loglevel", loglevelString) + Logger.SetLevel(logrus.ErrorLevel) + } +} + +func configureLocalFileSystemHook() { + + logPath := config.Cloki.Setting.LOG_SETTINGS.Path + logName := config.Cloki.Setting.LOG_SETTINGS.Name + var err error + + if configPath := os.Getenv("CLOKIAPPLOGPATH"); configPath != "" { + logPath = configPath + } + + if configName := os.Getenv("CLOKIAPPLOGNAME"); configName != "" { + logName = configName + } + + fileLogExtension := filepath.Ext(logName) + fileLogBase := strings.TrimSuffix(logName, fileLogExtension) + + pathAllLog := logPath + "/" + fileLogBase + "_%Y%m%d%H%M" + fileLogExtension + pathLog := logPath + "/" + logName + + RLogs, err = rotatelogs.New( + pathAllLog, + rotatelogs.WithLinkName(pathLog), + rotatelogs.WithMaxAge(time.Duration(config.Cloki.Setting.LOG_SETTINGS.MaxAgeDays)*time.Hour), + rotatelogs.WithRotationTime(time.Duration(config.Cloki.Setting.LOG_SETTINGS.RotationHours)*time.Hour), + ) + + if err != nil { + Logger.Println("Local file system hook initialize fail") + return + } + + Logger.SetOutput(RLogs) + log.SetOutput(RLogs) + + /* + Logger.AddHook(lfshook.NewHook(lfshook.WriterMap{ + logrus.InfoLevel: rLogs, + logrus.DebugLevel: rLogs, + logrus.ErrorLevel: rLogs, + }, &logrus.JSONFormatter{})) + */ +} +func configureSyslogHook() { + + var err error + + Logger.Println("Init syslog...") + + sevceritySyslog := getSevirtyByName(config.Cloki.Setting.LOG_SETTINGS.SysLogLevel) + + syslogger, err := syslog.New(sevceritySyslog, "hepic-app-server") + + //hook, err := lSyslog.NewSyslogHook(proto, logSyslogUri, sevceritySyslog, "") + + if err != nil { + Logger.Println("Unable to connect to syslog:", err) + } + + Logger.SetOutput(syslogger) + log.SetOutput(syslogger) + + /* + Logger.AddHook(lfshook.NewHook(lfshook.WriterMap{ + logrus.InfoLevel: rLogs, + logrus.DebugLevel: rLogs, + logrus.ErrorLevel: rLogs, + }, &logrus.JSONFormatter{})) + */ +} + +func Info(args ...interface{}) { + Logger.Info(args...) +} + +func Warning(args ...interface{}) { + Logger.Warning(args...) +} + +func Error(args ...interface{}) { + Logger.Error(args...) +} + +func Debug(args ...interface{}) { + Logger.Debug(args...) +} + +func getSevirtyByName(sevirity string) syslog.Priority { + + switch sevirity { + case SYSLOG_LOG_EMERG: + return syslog.LOG_EMERG + case SYSLOG_LOG_ALERT: + return syslog.LOG_ALERT + case SYSLOG_LOG_CRIT: + return syslog.LOG_CRIT + case SYSLOG_LOG_ERR: + return syslog.LOG_ERR + case SYSLOG_LOG_WARNING: + return syslog.LOG_WARNING + case SYSLOG_LOG_NOTICE: + return syslog.LOG_NOTICE + case SYSLOG_LOG_INFO: + return syslog.LOG_INFO + case SYSLOG_LOG_DEBUG: + return syslog.LOG_DEBUG + default: + return syslog.LOG_INFO + + } +} + +type qrynFormatter struct { + mtx sync.Mutex + formatter logrus.Formatter + bufferToQryn []*logrus.Entry + timer *time.Ticker + url string + app string + hostname string + headers map[string]string +} + +type qrynLogs struct { + Stream map[string]string `json:"stream"` + Values [][]string `json:"values"` +} + +func (q *qrynFormatter) Format(e *logrus.Entry) ([]byte, error) { + res, err := q.formatter.Format(e) + q.mtx.Lock() + q.bufferToQryn = append(q.bufferToQryn, e) + q.mtx.Unlock() + return res, err +} + +func (q *qrynFormatter) Run() { + q.timer = time.NewTicker(time.Second) + go func() { + for range q.timer.C { + q.mtx.Lock() + bufferToQryn := q.bufferToQryn + q.bufferToQryn = nil + q.mtx.Unlock() + if len(bufferToQryn) < 1 { + continue + } + + streams := map[string]*qrynLogs{} + for _, e := range bufferToQryn { + stream := map[string]string{} + stream["app"] = q.app + if q.hostname != "" { + stream["hostname"] = q.hostname + } + stream["level"] = e.Level.String() + + strStream := fmt.Sprintf("%v", stream) + if _, ok := streams[strStream]; !ok { + streams[strStream] = &qrynLogs{Stream: stream} + } + e.Buffer = nil + strValue, _ := q.formatter.Format(e) + streams[strStream].Values = append( + streams[strStream].Values, + []string{strconv.FormatInt(e.Time.UnixNano(), 10), string(strValue)}) + } + + var arrStreams []*qrynLogs + for _, s := range streams { + arrStreams = append(arrStreams, s) + } + + strStreams, _ := json.Marshal(map[string][]*qrynLogs{"streams": arrStreams}) + go func() { + req, _ := http.NewRequest("POST", q.url, bytes.NewReader(strStreams)) + if req == nil { + return + } + for k, v := range q.headers { + req.Header.Set(k, v) + } + req.Header.Set("Content-Type", "application/json") + http.DefaultClient.Do(req) + }() + } + }() +} diff --git a/writer/utils/logger/logger_race_test.go b/writer/utils/logger/logger_race_test.go new file mode 100644 index 00000000..cafada7d --- /dev/null +++ b/writer/utils/logger/logger_race_test.go @@ -0,0 +1,31 @@ +package logger + +import ( + "fmt" + "github.com/sirupsen/logrus" + "golang.org/x/sync/errgroup" + "testing" +) + +func TestLoggerRaceCond(t *testing.T) { + Logger.SetFormatter(&logrus.JSONFormatter{}) + qrynFmt := &qrynFormatter{ + formatter: Logger.Formatter, + url: "", + app: "", + hostname: "a", + headers: nil, + } + qrynFmt.Run() + Logger.SetFormatter(qrynFmt) + g := errgroup.Group{} + for i := 0; i < 10; i++ { + g.Go(func() error { + for j := 0; j < 100000; j++ { + Logger.Info("a", "B", fmt.Errorf("aaaa")) + } + return nil + }) + } + g.Wait() +} diff --git a/writer/utils/numbercache/cache.go b/writer/utils/numbercache/cache.go new file mode 100644 index 00000000..fd074574 --- /dev/null +++ b/writer/utils/numbercache/cache.go @@ -0,0 +1,74 @@ +package numbercache + +import ( + "github.com/VictoriaMetrics/fastcache" + "github.com/metrico/qryn/writer/model" + "sync" + "time" +) + +type ICache[T any] interface { + CheckAndSet(key T) bool + DB(db string) ICache[T] +} + +type Cache[K any] struct { + nodeMap map[string]*model.DataDatabasesMap + cleanup *time.Ticker + sets *fastcache.Cache + mtx *sync.Mutex + db []byte + isDistributed bool + serializer func(t K) []byte +} + +func (c *Cache[T]) CheckAndSet(key T) bool { + if c.isDistributed { + return false + } + c.mtx.Lock() + defer c.mtx.Unlock() + k := append(c.db, c.serializer(key)...) + if c.sets.Has(k) { + return true + } + c.sets.Set(k, []byte{1}) + return false +} + +func (c *Cache[T]) Stop() { + c.cleanup.Stop() +} + +func (c *Cache[T]) DB(db string) ICache[T] { + return &Cache[T]{ + isDistributed: c.nodeMap[db].ClusterName != "", + nodeMap: c.nodeMap, + sets: c.sets, + mtx: c.mtx, + db: []byte(db), + serializer: c.serializer, + } +} + +func NewCache[T comparable](TTL time.Duration, serializer func(val T) []byte, + nodeMap map[string]*model.DataDatabasesMap) *Cache[T] { + if serializer == nil { + panic("NO SER") + } + res := Cache[T]{ + nodeMap: nodeMap, + cleanup: time.NewTicker(TTL), + sets: fastcache.New(100 * 1024 * 1024), + mtx: &sync.Mutex{}, + serializer: serializer, + } + go func() { + for _ = range res.cleanup.C { + res.mtx.Lock() + res.sets.Reset() + res.mtx.Unlock() + } + }() + return &res +} diff --git a/writer/utils/numbercache/dynamicCache.go b/writer/utils/numbercache/dynamicCache.go new file mode 100644 index 00000000..fd010d86 --- /dev/null +++ b/writer/utils/numbercache/dynamicCache.go @@ -0,0 +1,44 @@ +package numbercache + +import ( + "github.com/VictoriaMetrics/fastcache" + "sync" + "time" +) + +type DynamicCache[K any] struct { + cleanup *time.Ticker + sets *fastcache.Cache + mtx *sync.Mutex + db []byte + isDistributed bool + serializer func(t K) []byte +} + +func (c *DynamicCache[T]) CheckAndSet(key T) bool { + if c.isDistributed { + return false + } + c.mtx.Lock() + defer c.mtx.Unlock() + k := append(c.db, c.serializer(key)...) + if c.sets.Has(k) { + return true + } + c.sets.Set(k, []byte{1}) + return false +} + +func (c *DynamicCache[T]) Stop() { + c.cleanup.Stop() +} + +func (c *DynamicCache[T]) DB(db string) ICache[T] { + return &DynamicCache[T]{ + isDistributed: db[:2] == "c-", + sets: c.sets, + mtx: c.mtx, + db: []byte(db), + serializer: c.serializer, + } +} diff --git a/writer/utils/promise/promise.go b/writer/utils/promise/promise.go new file mode 100644 index 00000000..800bce7d --- /dev/null +++ b/writer/utils/promise/promise.go @@ -0,0 +1,59 @@ +package promise + +import ( + "context" + "fmt" + "sync/atomic" +) + +var GetContextTimeout = fmt.Errorf("get operation context timed out") + +type Promise[T any] struct { + lock chan any + err error + res T + pending int32 +} + +func New[T any]() *Promise[T] { + res := &Promise[T]{ + lock: make(chan any), + pending: 1, + } + return res +} + +func Fulfilled[T any](err error, res T) *Promise[T] { + l := make(chan any) + close(l) + return &Promise[T]{ + lock: l, + err: err, + res: res, + pending: 0, + } +} + +func (p *Promise[T]) Get() (T, error) { + <-p.lock + return p.res, p.err +} + +func (p *Promise[T]) GetCtx(ctx context.Context) (T, error) { + select { + case <-ctx.Done(): + var res T + return res, GetContextTimeout + case <-p.lock: + return p.res, p.err + } +} + +func (p *Promise[T]) Done(res T, err error) { + if !atomic.CompareAndSwapInt32(&p.pending, 1, 0) { + return + } + p.res = res + p.err = err + close(p.lock) +} diff --git a/writer/utils/promise/promise_test.go b/writer/utils/promise/promise_test.go new file mode 100644 index 00000000..ca182e87 --- /dev/null +++ b/writer/utils/promise/promise_test.go @@ -0,0 +1,23 @@ +package promise + +import ( + "sync" + "testing" +) + +func BenchmarkPromise(b *testing.B) { + wg := sync.WaitGroup{} + promises := make([]*Promise[int], b.N) + for i := 0; i < b.N; i++ { + wg.Add(1) + promises[i] = New[int]() + go func(p *Promise[int]) { + defer wg.Done() + p.Get() + }(promises[i]) + } + for _, p := range promises { + p.Done(1, nil) + } + wg.Wait() +} diff --git a/writer/utils/proto/logproto/loki.pb.go b/writer/utils/proto/logproto/loki.pb.go new file mode 100644 index 00000000..b99d888a --- /dev/null +++ b/writer/utils/proto/logproto/loki.pb.go @@ -0,0 +1,424 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0-devel +// protoc v3.14.0 +// source: loki.proto + +package logproto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Timestamp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Represents seconds of UTC time since Unix epoch + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + // 9999-12-31T23:59:59Z inclusive. + Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"` + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 + // inclusive. + Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` +} + +func (x *Timestamp) Reset() { + *x = Timestamp{} + if protoimpl.UnsafeEnabled { + mi := &file_loki_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Timestamp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Timestamp) ProtoMessage() {} + +func (x *Timestamp) ProtoReflect() protoreflect.Message { + mi := &file_loki_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Timestamp.ProtoReflect.Descriptor instead. +func (*Timestamp) Descriptor() ([]byte, []int) { + return file_loki_proto_rawDescGZIP(), []int{0} +} + +func (x *Timestamp) GetSeconds() int64 { + if x != nil { + return x.Seconds + } + return 0 +} + +func (x *Timestamp) GetNanos() int32 { + if x != nil { + return x.Nanos + } + return 0 +} + +type PushRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Streams []*StreamAdapter `protobuf:"bytes,1,rep,name=streams,proto3" json:"streams,omitempty"` +} + +func (x *PushRequest) Reset() { + *x = PushRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_loki_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PushRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PushRequest) ProtoMessage() {} + +func (x *PushRequest) ProtoReflect() protoreflect.Message { + mi := &file_loki_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PushRequest.ProtoReflect.Descriptor instead. +func (*PushRequest) Descriptor() ([]byte, []int) { + return file_loki_proto_rawDescGZIP(), []int{1} +} + +func (x *PushRequest) GetStreams() []*StreamAdapter { + if x != nil { + return x.Streams + } + return nil +} + +type PushResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *PushResponse) Reset() { + *x = PushResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_loki_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PushResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PushResponse) ProtoMessage() {} + +func (x *PushResponse) ProtoReflect() protoreflect.Message { + mi := &file_loki_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PushResponse.ProtoReflect.Descriptor instead. +func (*PushResponse) Descriptor() ([]byte, []int) { + return file_loki_proto_rawDescGZIP(), []int{2} +} + +type StreamAdapter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Labels string `protobuf:"bytes,1,opt,name=labels,proto3" json:"labels,omitempty"` + Entries []*EntryAdapter `protobuf:"bytes,2,rep,name=entries,proto3" json:"entries,omitempty"` +} + +func (x *StreamAdapter) Reset() { + *x = StreamAdapter{} + if protoimpl.UnsafeEnabled { + mi := &file_loki_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StreamAdapter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamAdapter) ProtoMessage() {} + +func (x *StreamAdapter) ProtoReflect() protoreflect.Message { + mi := &file_loki_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamAdapter.ProtoReflect.Descriptor instead. +func (*StreamAdapter) Descriptor() ([]byte, []int) { + return file_loki_proto_rawDescGZIP(), []int{3} +} + +func (x *StreamAdapter) GetLabels() string { + if x != nil { + return x.Labels + } + return "" +} + +func (x *StreamAdapter) GetEntries() []*EntryAdapter { + if x != nil { + return x.Entries + } + return nil +} + +type EntryAdapter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp *Timestamp `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Line string `protobuf:"bytes,2,opt,name=line,proto3" json:"line,omitempty"` +} + +func (x *EntryAdapter) Reset() { + *x = EntryAdapter{} + if protoimpl.UnsafeEnabled { + mi := &file_loki_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EntryAdapter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EntryAdapter) ProtoMessage() {} + +func (x *EntryAdapter) ProtoReflect() protoreflect.Message { + mi := &file_loki_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EntryAdapter.ProtoReflect.Descriptor instead. +func (*EntryAdapter) Descriptor() ([]byte, []int) { + return file_loki_proto_rawDescGZIP(), []int{4} +} + +func (x *EntryAdapter) GetTimestamp() *Timestamp { + if x != nil { + return x.Timestamp + } + return nil +} + +func (x *EntryAdapter) GetLine() string { + if x != nil { + return x.Line + } + return "" +} + +var File_loki_proto protoreflect.FileDescriptor + +var file_loki_proto_rawDesc = []byte{ + 0x0a, 0x0a, 0x6c, 0x6f, 0x6b, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x6c, 0x6f, + 0x67, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3b, 0x0a, 0x09, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x14, 0x0a, + 0x05, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6e, 0x61, + 0x6e, 0x6f, 0x73, 0x22, 0x40, 0x0a, 0x0b, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x31, 0x0a, 0x07, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x6f, 0x67, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x64, 0x61, 0x70, 0x74, 0x65, 0x72, 0x52, 0x07, 0x73, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x73, 0x22, 0x0e, 0x0a, 0x0c, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x59, 0x0a, 0x0d, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, + 0x64, 0x61, 0x70, 0x74, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x30, + 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x6c, 0x6f, 0x67, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x41, 0x64, 0x61, 0x70, 0x74, 0x65, 0x72, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, + 0x22, 0x55, 0x0a, 0x0c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x41, 0x64, 0x61, 0x70, 0x74, 0x65, 0x72, + 0x12, 0x31, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6f, 0x67, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x0a, 0x5a, 0x08, 0x6c, 0x6f, 0x67, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_loki_proto_rawDescOnce sync.Once + file_loki_proto_rawDescData = file_loki_proto_rawDesc +) + +func file_loki_proto_rawDescGZIP() []byte { + file_loki_proto_rawDescOnce.Do(func() { + file_loki_proto_rawDescData = protoimpl.X.CompressGZIP(file_loki_proto_rawDescData) + }) + return file_loki_proto_rawDescData +} + +var file_loki_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_loki_proto_goTypes = []interface{}{ + (*Timestamp)(nil), // 0: logproto.Timestamp + (*PushRequest)(nil), // 1: logproto.PushRequest + (*PushResponse)(nil), // 2: logproto.PushResponse + (*StreamAdapter)(nil), // 3: logproto.StreamAdapter + (*EntryAdapter)(nil), // 4: logproto.EntryAdapter +} +var file_loki_proto_depIdxs = []int32{ + 3, // 0: logproto.PushRequest.streams:type_name -> logproto.StreamAdapter + 4, // 1: logproto.StreamAdapter.entries:type_name -> logproto.EntryAdapter + 0, // 2: logproto.EntryAdapter.timestamp:type_name -> logproto.Timestamp + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_loki_proto_init() } +func file_loki_proto_init() { + if File_loki_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_loki_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Timestamp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_loki_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PushRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_loki_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PushResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_loki_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StreamAdapter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_loki_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EntryAdapter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_loki_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_loki_proto_goTypes, + DependencyIndexes: file_loki_proto_depIdxs, + MessageInfos: file_loki_proto_msgTypes, + }.Build() + File_loki_proto = out.File + file_loki_proto_rawDesc = nil + file_loki_proto_goTypes = nil + file_loki_proto_depIdxs = nil +} diff --git a/lib/loki.proto b/writer/utils/proto/loki.proto similarity index 100% rename from lib/loki.proto rename to writer/utils/proto/loki.proto diff --git a/lib/prompb.proto b/writer/utils/proto/prompb.proto similarity index 100% rename from lib/prompb.proto rename to writer/utils/proto/prompb.proto diff --git a/writer/utils/proto/prompb/prompb.pb.go b/writer/utils/proto/prompb/prompb.pb.go new file mode 100644 index 00000000..ff4faa2d --- /dev/null +++ b/writer/utils/proto/prompb/prompb.pb.go @@ -0,0 +1,957 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0-devel +// protoc v3.14.0 +// source: prompb.proto + +package prompb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type LabelMatcher_Type int32 + +const ( + LabelMatcher_EQ LabelMatcher_Type = 0 + LabelMatcher_NEQ LabelMatcher_Type = 1 + LabelMatcher_RE LabelMatcher_Type = 2 + LabelMatcher_NRE LabelMatcher_Type = 3 +) + +// Enum value maps for LabelMatcher_Type. +var ( + LabelMatcher_Type_name = map[int32]string{ + 0: "EQ", + 1: "NEQ", + 2: "RE", + 3: "NRE", + } + LabelMatcher_Type_value = map[string]int32{ + "EQ": 0, + "NEQ": 1, + "RE": 2, + "NRE": 3, + } +) + +func (x LabelMatcher_Type) Enum() *LabelMatcher_Type { + p := new(LabelMatcher_Type) + *p = x + return p +} + +func (x LabelMatcher_Type) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (LabelMatcher_Type) Descriptor() protoreflect.EnumDescriptor { + return file_prompb_proto_enumTypes[0].Descriptor() +} + +func (LabelMatcher_Type) Type() protoreflect.EnumType { + return &file_prompb_proto_enumTypes[0] +} + +func (x LabelMatcher_Type) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use LabelMatcher_Type.Descriptor instead. +func (LabelMatcher_Type) EnumDescriptor() ([]byte, []int) { + return file_prompb_proto_rawDescGZIP(), []int{9, 0} +} + +type WriteRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timeseries []*TimeSeries `protobuf:"bytes,1,rep,name=timeseries,proto3" json:"timeseries,omitempty"` +} + +func (x *WriteRequest) Reset() { + *x = WriteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_prompb_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WriteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WriteRequest) ProtoMessage() {} + +func (x *WriteRequest) ProtoReflect() protoreflect.Message { + mi := &file_prompb_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WriteRequest.ProtoReflect.Descriptor instead. +func (*WriteRequest) Descriptor() ([]byte, []int) { + return file_prompb_proto_rawDescGZIP(), []int{0} +} + +func (x *WriteRequest) GetTimeseries() []*TimeSeries { + if x != nil { + return x.Timeseries + } + return nil +} + +type ReadRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Queries []*Query `protobuf:"bytes,1,rep,name=queries,proto3" json:"queries,omitempty"` +} + +func (x *ReadRequest) Reset() { + *x = ReadRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_prompb_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadRequest) ProtoMessage() {} + +func (x *ReadRequest) ProtoReflect() protoreflect.Message { + mi := &file_prompb_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadRequest.ProtoReflect.Descriptor instead. +func (*ReadRequest) Descriptor() ([]byte, []int) { + return file_prompb_proto_rawDescGZIP(), []int{1} +} + +func (x *ReadRequest) GetQueries() []*Query { + if x != nil { + return x.Queries + } + return nil +} + +type ReadResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // In same order as the request's queries. + Results []*QueryResult `protobuf:"bytes,1,rep,name=results,proto3" json:"results,omitempty"` +} + +func (x *ReadResponse) Reset() { + *x = ReadResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_prompb_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadResponse) ProtoMessage() {} + +func (x *ReadResponse) ProtoReflect() protoreflect.Message { + mi := &file_prompb_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadResponse.ProtoReflect.Descriptor instead. +func (*ReadResponse) Descriptor() ([]byte, []int) { + return file_prompb_proto_rawDescGZIP(), []int{2} +} + +func (x *ReadResponse) GetResults() []*QueryResult { + if x != nil { + return x.Results + } + return nil +} + +type Query struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StartTimestampMs int64 `protobuf:"varint,1,opt,name=start_timestamp_ms,json=startTimestampMs,proto3" json:"start_timestamp_ms,omitempty"` + EndTimestampMs int64 `protobuf:"varint,2,opt,name=end_timestamp_ms,json=endTimestampMs,proto3" json:"end_timestamp_ms,omitempty"` + Matchers []*LabelMatcher `protobuf:"bytes,3,rep,name=matchers,proto3" json:"matchers,omitempty"` + Hints *ReadHints `protobuf:"bytes,4,opt,name=hints,proto3" json:"hints,omitempty"` +} + +func (x *Query) Reset() { + *x = Query{} + if protoimpl.UnsafeEnabled { + mi := &file_prompb_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Query) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Query) ProtoMessage() {} + +func (x *Query) ProtoReflect() protoreflect.Message { + mi := &file_prompb_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Query.ProtoReflect.Descriptor instead. +func (*Query) Descriptor() ([]byte, []int) { + return file_prompb_proto_rawDescGZIP(), []int{3} +} + +func (x *Query) GetStartTimestampMs() int64 { + if x != nil { + return x.StartTimestampMs + } + return 0 +} + +func (x *Query) GetEndTimestampMs() int64 { + if x != nil { + return x.EndTimestampMs + } + return 0 +} + +func (x *Query) GetMatchers() []*LabelMatcher { + if x != nil { + return x.Matchers + } + return nil +} + +func (x *Query) GetHints() *ReadHints { + if x != nil { + return x.Hints + } + return nil +} + +type QueryResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Samples within a time series must be ordered by time. + Timeseries []*TimeSeries `protobuf:"bytes,1,rep,name=timeseries,proto3" json:"timeseries,omitempty"` +} + +func (x *QueryResult) Reset() { + *x = QueryResult{} + if protoimpl.UnsafeEnabled { + mi := &file_prompb_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *QueryResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryResult) ProtoMessage() {} + +func (x *QueryResult) ProtoReflect() protoreflect.Message { + mi := &file_prompb_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QueryResult.ProtoReflect.Descriptor instead. +func (*QueryResult) Descriptor() ([]byte, []int) { + return file_prompb_proto_rawDescGZIP(), []int{4} +} + +func (x *QueryResult) GetTimeseries() []*TimeSeries { + if x != nil { + return x.Timeseries + } + return nil +} + +type Sample struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Value float64 `protobuf:"fixed64,1,opt,name=value,proto3" json:"value,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (x *Sample) Reset() { + *x = Sample{} + if protoimpl.UnsafeEnabled { + mi := &file_prompb_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Sample) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Sample) ProtoMessage() {} + +func (x *Sample) ProtoReflect() protoreflect.Message { + mi := &file_prompb_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Sample.ProtoReflect.Descriptor instead. +func (*Sample) Descriptor() ([]byte, []int) { + return file_prompb_proto_rawDescGZIP(), []int{5} +} + +func (x *Sample) GetValue() float64 { + if x != nil { + return x.Value + } + return 0 +} + +func (x *Sample) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +type TimeSeries struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Labels []*Label `protobuf:"bytes,1,rep,name=labels,proto3" json:"labels,omitempty"` + Samples []*Sample `protobuf:"bytes,2,rep,name=samples,proto3" json:"samples,omitempty"` +} + +func (x *TimeSeries) Reset() { + *x = TimeSeries{} + if protoimpl.UnsafeEnabled { + mi := &file_prompb_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TimeSeries) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TimeSeries) ProtoMessage() {} + +func (x *TimeSeries) ProtoReflect() protoreflect.Message { + mi := &file_prompb_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TimeSeries.ProtoReflect.Descriptor instead. +func (*TimeSeries) Descriptor() ([]byte, []int) { + return file_prompb_proto_rawDescGZIP(), []int{6} +} + +func (x *TimeSeries) GetLabels() []*Label { + if x != nil { + return x.Labels + } + return nil +} + +func (x *TimeSeries) GetSamples() []*Sample { + if x != nil { + return x.Samples + } + return nil +} + +type Label struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *Label) Reset() { + *x = Label{} + if protoimpl.UnsafeEnabled { + mi := &file_prompb_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Label) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Label) ProtoMessage() {} + +func (x *Label) ProtoReflect() protoreflect.Message { + mi := &file_prompb_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Label.ProtoReflect.Descriptor instead. +func (*Label) Descriptor() ([]byte, []int) { + return file_prompb_proto_rawDescGZIP(), []int{7} +} + +func (x *Label) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Label) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type Labels struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Labels []*Label `protobuf:"bytes,1,rep,name=labels,proto3" json:"labels,omitempty"` +} + +func (x *Labels) Reset() { + *x = Labels{} + if protoimpl.UnsafeEnabled { + mi := &file_prompb_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Labels) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Labels) ProtoMessage() {} + +func (x *Labels) ProtoReflect() protoreflect.Message { + mi := &file_prompb_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Labels.ProtoReflect.Descriptor instead. +func (*Labels) Descriptor() ([]byte, []int) { + return file_prompb_proto_rawDescGZIP(), []int{8} +} + +func (x *Labels) GetLabels() []*Label { + if x != nil { + return x.Labels + } + return nil +} + +// Matcher specifies a rule, which can match or set of labels or not. +type LabelMatcher struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type LabelMatcher_Type `protobuf:"varint,1,opt,name=type,proto3,enum=prometheus.LabelMatcher_Type" json:"type,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *LabelMatcher) Reset() { + *x = LabelMatcher{} + if protoimpl.UnsafeEnabled { + mi := &file_prompb_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LabelMatcher) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LabelMatcher) ProtoMessage() {} + +func (x *LabelMatcher) ProtoReflect() protoreflect.Message { + mi := &file_prompb_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LabelMatcher.ProtoReflect.Descriptor instead. +func (*LabelMatcher) Descriptor() ([]byte, []int) { + return file_prompb_proto_rawDescGZIP(), []int{9} +} + +func (x *LabelMatcher) GetType() LabelMatcher_Type { + if x != nil { + return x.Type + } + return LabelMatcher_EQ +} + +func (x *LabelMatcher) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *LabelMatcher) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type ReadHints struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StepMs int64 `protobuf:"varint,1,opt,name=step_ms,json=stepMs,proto3" json:"step_ms,omitempty"` // Query step size in milliseconds. + Func string `protobuf:"bytes,2,opt,name=func,proto3" json:"func,omitempty"` // String representation of surrounding function or aggregation. + StartMs int64 `protobuf:"varint,3,opt,name=start_ms,json=startMs,proto3" json:"start_ms,omitempty"` // Start time in milliseconds. + EndMs int64 `protobuf:"varint,4,opt,name=end_ms,json=endMs,proto3" json:"end_ms,omitempty"` // End time in milliseconds. +} + +func (x *ReadHints) Reset() { + *x = ReadHints{} + if protoimpl.UnsafeEnabled { + mi := &file_prompb_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadHints) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadHints) ProtoMessage() {} + +func (x *ReadHints) ProtoReflect() protoreflect.Message { + mi := &file_prompb_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadHints.ProtoReflect.Descriptor instead. +func (*ReadHints) Descriptor() ([]byte, []int) { + return file_prompb_proto_rawDescGZIP(), []int{10} +} + +func (x *ReadHints) GetStepMs() int64 { + if x != nil { + return x.StepMs + } + return 0 +} + +func (x *ReadHints) GetFunc() string { + if x != nil { + return x.Func + } + return "" +} + +func (x *ReadHints) GetStartMs() int64 { + if x != nil { + return x.StartMs + } + return 0 +} + +func (x *ReadHints) GetEndMs() int64 { + if x != nil { + return x.EndMs + } + return 0 +} + +var File_prompb_proto protoreflect.FileDescriptor + +var file_prompb_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, + 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x22, 0x46, 0x0a, 0x0c, 0x57, 0x72, + 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x0a, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x22, 0x3a, 0x0a, 0x0b, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x2b, 0x0a, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x22, 0x41, + 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, + 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x73, 0x22, 0xc2, 0x01, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x2c, 0x0a, 0x12, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6d, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4d, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x65, 0x6e, 0x64, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0e, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x4d, 0x73, 0x12, 0x34, 0x0a, 0x08, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, + 0x75, 0x73, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x52, + 0x08, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x73, 0x12, 0x2b, 0x0a, 0x05, 0x68, 0x69, 0x6e, + 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, + 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x52, + 0x05, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x45, 0x0a, 0x0b, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x36, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x6d, + 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, + 0x73, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x22, 0x3c, 0x0a, + 0x06, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x65, 0x0a, 0x0a, 0x54, + 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x6c, 0x61, 0x62, + 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x6d, + 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x06, 0x6c, 0x61, + 0x62, 0x65, 0x6c, 0x73, 0x12, 0x2c, 0x0a, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, + 0x75, 0x73, 0x2e, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x73, 0x22, 0x31, 0x0a, 0x05, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x33, 0x0a, 0x06, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, + 0x29, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x4c, 0x61, 0x62, + 0x65, 0x6c, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x22, 0x95, 0x01, 0x0a, 0x0c, 0x4c, + 0x61, 0x62, 0x65, 0x6c, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x6d, + 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x4d, 0x61, 0x74, 0x63, + 0x68, 0x65, 0x72, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x28, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x06, 0x0a, 0x02, 0x45, 0x51, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4e, 0x45, 0x51, 0x10, + 0x01, 0x12, 0x06, 0x0a, 0x02, 0x52, 0x45, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x4e, 0x52, 0x45, + 0x10, 0x03, 0x22, 0x6a, 0x0a, 0x09, 0x52, 0x65, 0x61, 0x64, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x12, + 0x17, 0x0a, 0x07, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x06, 0x73, 0x74, 0x65, 0x70, 0x4d, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x75, 0x6e, 0x63, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x12, 0x19, 0x0a, 0x08, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x4d, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e, 0x64, 0x5f, 0x6d, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x42, 0x08, + 0x5a, 0x06, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_prompb_proto_rawDescOnce sync.Once + file_prompb_proto_rawDescData = file_prompb_proto_rawDesc +) + +func file_prompb_proto_rawDescGZIP() []byte { + file_prompb_proto_rawDescOnce.Do(func() { + file_prompb_proto_rawDescData = protoimpl.X.CompressGZIP(file_prompb_proto_rawDescData) + }) + return file_prompb_proto_rawDescData +} + +var file_prompb_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_prompb_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_prompb_proto_goTypes = []interface{}{ + (LabelMatcher_Type)(0), // 0: prometheus.LabelMatcher.Type + (*WriteRequest)(nil), // 1: prometheus.WriteRequest + (*ReadRequest)(nil), // 2: prometheus.ReadRequest + (*ReadResponse)(nil), // 3: prometheus.ReadResponse + (*Query)(nil), // 4: prometheus.Query + (*QueryResult)(nil), // 5: prometheus.QueryResult + (*Sample)(nil), // 6: prometheus.Sample + (*TimeSeries)(nil), // 7: prometheus.TimeSeries + (*Label)(nil), // 8: prometheus.Label + (*Labels)(nil), // 9: prometheus.Labels + (*LabelMatcher)(nil), // 10: prometheus.LabelMatcher + (*ReadHints)(nil), // 11: prometheus.ReadHints +} +var file_prompb_proto_depIdxs = []int32{ + 7, // 0: prometheus.WriteRequest.timeseries:type_name -> prometheus.TimeSeries + 4, // 1: prometheus.ReadRequest.queries:type_name -> prometheus.Query + 5, // 2: prometheus.ReadResponse.results:type_name -> prometheus.QueryResult + 10, // 3: prometheus.Query.matchers:type_name -> prometheus.LabelMatcher + 11, // 4: prometheus.Query.hints:type_name -> prometheus.ReadHints + 7, // 5: prometheus.QueryResult.timeseries:type_name -> prometheus.TimeSeries + 8, // 6: prometheus.TimeSeries.labels:type_name -> prometheus.Label + 6, // 7: prometheus.TimeSeries.samples:type_name -> prometheus.Sample + 8, // 8: prometheus.Labels.labels:type_name -> prometheus.Label + 0, // 9: prometheus.LabelMatcher.type:type_name -> prometheus.LabelMatcher.Type + 10, // [10:10] is the sub-list for method output_type + 10, // [10:10] is the sub-list for method input_type + 10, // [10:10] is the sub-list for extension type_name + 10, // [10:10] is the sub-list for extension extendee + 0, // [0:10] is the sub-list for field type_name +} + +func init() { file_prompb_proto_init() } +func file_prompb_proto_init() { + if File_prompb_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_prompb_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WriteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_prompb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_prompb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_prompb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Query); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_prompb_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QueryResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_prompb_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Sample); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_prompb_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TimeSeries); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_prompb_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Label); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_prompb_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Labels); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_prompb_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LabelMatcher); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_prompb_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadHints); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_prompb_proto_rawDesc, + NumEnums: 1, + NumMessages: 11, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_prompb_proto_goTypes, + DependencyIndexes: file_prompb_proto_depIdxs, + EnumInfos: file_prompb_proto_enumTypes, + MessageInfos: file_prompb_proto_msgTypes, + }.Build() + File_prompb_proto = out.File + file_prompb_proto_rawDesc = nil + file_prompb_proto_goTypes = nil + file_prompb_proto_depIdxs = nil +} diff --git a/writer/utils/shutdown/shutdown.go b/writer/utils/shutdown/shutdown.go new file mode 100644 index 00000000..fce19f7b --- /dev/null +++ b/writer/utils/shutdown/shutdown.go @@ -0,0 +1,7 @@ +package shutdown + +func Shutdown(code int) { + Chan <- code +} + +var Chan = make(chan int) diff --git a/writer/utils/stat/stat.go b/writer/utils/stat/stat.go new file mode 100644 index 00000000..6577dca0 --- /dev/null +++ b/writer/utils/stat/stat.go @@ -0,0 +1,186 @@ +package stat + +import ( + "github.com/metrico/qryn/writer/metric" + "github.com/metrico/qryn/writer/utils/proto/prompb" + "math" + "regexp" + "strings" + "sync" + "time" +) + +var metricsMtx = sync.Mutex{} + +const timeSpanSec = 30 + +var sentMetrics = func() []map[string]int64 { + res := make([]map[string]int64, timeSpanSec+2) + for i := 0; i < 4; i++ { + res[i] = make(map[string]int64, timeSpanSec+2) + } + return res +}() + +var counters = map[string]int64{} + +func getOrDefault(idx int64, name string, def int64) int64 { + if _, ok := sentMetrics[idx][name]; !ok { + return def + } + return sentMetrics[idx][name] +} + +func getIdx(time2 time.Time) int64 { + return time2.Unix() % (timeSpanSec + 2) +} + +func checkMap(idx int64) { + if sentMetrics[idx] == nil { + sentMetrics[idx] = make(map[string]int64, 20) + } +} + +func AddSentMetrics(name string, count int64) { + metricsMtx.Lock() + defer metricsMtx.Unlock() + idx := getIdx(time.Now()) + checkMap(idx) + if _, ok := sentMetrics[idx][name]; !ok { + sentMetrics[idx][name] = 0 + } + sentMetrics[idx][name] = sentMetrics[idx][name] + count + if _, ok := counters[name+"_counter"]; ok { + counters[name+"_counter"] += count + } else { + counters[name+"_counter"] = count + } + // Define a map of metric handlers for different conditions + metricHandlers := map[string]func(int64){ + "json_parse_errors": func(count int64) { + metric.JsonParseErrors.Add(float64(count)) + }, + "connection_reset_by_peer": func(count int64) { + metric.ConnectionResetByPeer.Add(float64(count)) + }, + } + if strings.HasSuffix(name, "_sent_rows") { + name = strings.Replace(name, "_sent_rows", "", -1) + metric.SentRows.WithLabelValues(name).Add(float64(count)) + } else if strings.HasSuffix(name, "_sent_bytes") { + name = strings.Replace(name, "_sent_bytes", "", -1) + metric.SentBytes.WithLabelValues(name).Add(float64(count)) + } else if handler, exists := metricHandlers[name]; exists { + handler(count) + } +} + +func AddCompoundMetric(name string, count int64) { + metricsMtx.Lock() + defer metricsMtx.Unlock() + idx := getIdx(time.Now()) + checkMap(idx) + max := getOrDefault(idx, name+"_max", math.MinInt64) + if max < count { + max = count + } + min := getOrDefault(idx, name+"_min", math.MaxInt) + if min > count { + min = count + } + sum := getOrDefault(idx, name+"_sum", 0) + count + cnt := getOrDefault(idx, name+"_count", 0) + 1 + sentMetrics[idx][name+"_max"] = max + sentMetrics[idx][name+"_min"] = min + sentMetrics[idx][name+"_sum"] = sum + sentMetrics[idx][name+"_count"] = cnt + if strings.Contains(name, "tx_close_time_ms") { + metric.TxCloseTime.Observe(float64(count)) // Adjust as needed for labeling + } else { + metric.SendTime.Observe(float64(count)) + } + +} + +func GetRate() map[string]int64 { + metricsMtx.Lock() + defer metricsMtx.Unlock() + return getRate() +} + +func getRate() map[string]int64 { + end := time.Now() + start := end.Add(time.Second * -31) + res := make(map[string]int64, 100) + for i := start; i.Before(end); i = i.Add(time.Second) { + idx := getIdx(i) + checkMap(idx) + for k, v := range sentMetrics[idx] { + if _, ok := res[k]; !ok { + res[k] = v + continue + } + if strings.HasSuffix(k, "_max") { + if res[k] < v { + res[k] = v + } + continue + } + if strings.HasSuffix(k, "_min") { + if res[k] > v { + res[k] = v + } + continue + } + res[k] += v + } + } + + for k, v := range counters { + res[k] = v + } + return res +} + +var nameSanitizer = regexp.MustCompile("\\W") + +func SanitizeName(name string) string { + return strings.ToLower(nameSanitizer.ReplaceAllString(name, "_")) +} + +func GetRemoteWrite() *prompb.WriteRequest { + metricsMtx.Lock() + defer metricsMtx.Unlock() + req := prompb.WriteRequest{ + Timeseries: make([]*prompb.TimeSeries, 0, 50), + } + now := time.Now().UnixMilli() - 2000 + for k, v := range getRate() { + ts := prompb.TimeSeries{ + Labels: []*prompb.Label{{ + Name: "__name__", + Value: SanitizeName(k), + }}, + Samples: []*prompb.Sample{{Timestamp: now, Value: float64(v)}}, + } + req.Timeseries = append(req.Timeseries, &ts) + } + for k, v := range counters { + ts := prompb.TimeSeries{ + Labels: []*prompb.Label{{ + Name: "__name__", + Value: SanitizeName(k), + }}, + Samples: []*prompb.Sample{{Timestamp: now, Value: float64(v)}}, + } + req.Timeseries = append(req.Timeseries, &ts) + } + return &req +} + +func ResetRate() { + metricsMtx.Lock() + defer metricsMtx.Unlock() + idx := getIdx(time.Now().Add(time.Second)) + sentMetrics[idx] = make(map[string]int64, 20) +} diff --git a/writer/utils/unmarshal/builder.go b/writer/utils/unmarshal/builder.go new file mode 100644 index 00000000..e70a70a8 --- /dev/null +++ b/writer/utils/unmarshal/builder.go @@ -0,0 +1,502 @@ +package unmarshal + +import ( + "context" + "fmt" + "github.com/go-faster/city" + "unsafe" + //"github.com/metrico/qryn/writer/fingerprints_limiter" + "github.com/metrico/qryn/writer/model" + //customErrors "github.com/metrico/qryn/writer/utils/errors" + "github.com/metrico/qryn/writer/utils/logger" + "github.com/metrico/qryn/writer/utils/numbercache" + "google.golang.org/protobuf/proto" + "io" + "runtime/debug" + "strconv" + "time" +) + +// OrgChecker defines the interface for checking fingerprints. +type OrgChecker interface { + IsFPAllowed(fp uint64) bool + IsSus() bool +} + +type OrgCheckerFactory interface { + CreateOrgChecker() OrgChecker +} + +type onEntriesHandler func(labels [][]string, timestampsNS []int64, + message []string, value []float64, types []uint8) error + +type onProfileHandler func(timestampNs uint64, + Type string, + serviceName string, + samplesTypesUnits []model.StrStr, periodType string, + periodUnit string, tags []model.StrStr, + durationNs uint64, payloadType string, payload []byte, + valuersAgg []model.ValuesAgg, + tree []model.TreeRootStructure, functions []model.Function) error + +type onSpanHandler func(traceId []byte, spanId []byte, timestampNs int64, durationNs int64, + parentId string, name string, serviceName string, payload []byte, key []string, val []string) error + +type ParsingFunction func(ctx context.Context, body io.Reader, + fpCache numbercache.ICache[uint64]) chan *model.ParserResponse + +type ParserCtx struct { + bodyReader io.Reader + bodyBuffer []byte + bodyObject interface{} + fpCache numbercache.ICache[uint64] + ctx context.Context + ctxMap map[string]string + queryParams map[string]string +} + +type parserFn func(ctx *ParserCtx) error + +type iLogsParser interface { + Decode() error + SetOnEntries(h onEntriesHandler) +} + +type iProfilesParser interface { + Decode() error + SetOnProfile(h onProfileHandler) +} +type iSpansParser interface { + Decode() error + SetOnEntry(h onSpanHandler) +} + +type parserBuilder struct { + PreParse []parserFn + LogsParser func(ctx *ParserCtx) iLogsParser + ProfileParser func(ctx *ParserCtx) iProfilesParser + SpansParser func(ctx *ParserCtx) iSpansParser + payloadType int8 +} + +type fpsCache map[int64]map[uint64]bool + +func newFpsCache() fpsCache { + return make(fpsCache) +} + +func (c fpsCache) CheckAndSet(date time.Time, fp uint64) bool { + res := false + day, ok := c[date.Unix()] + if !ok { + day = make(map[uint64]bool) + c[date.Unix()] = day + res = true + } + _, ok = c[date.Unix()][fp] + if !ok { + res = true + c[date.Unix()][fp] = true + } + return res +} + +type parserDoer struct { + PreParse []parserFn + LogsParser iLogsParser + SpansParser iSpansParser + ProfileParser iProfilesParser + ctx *ParserCtx + ttlDays uint16 + + res chan *model.ParserResponse + tsSpl *timeSeriesAndSamples + size int + payloadType int8 + profile *model.ProfileData + spans *model.TempoSamples + attrs *model.TempoTag +} + +func (p *parserDoer) Do() chan *model.ParserResponse { + p.res = make(chan *model.ParserResponse) + for _, fn := range p.PreParse { + err := fn(p.ctx) + if err != nil { + go func() { p.res <- &model.ParserResponse{Error: err}; close(p.res) }() + return p.res + } + } + + if p.LogsParser != nil { + p.doParseLogs() + } else if p.SpansParser != nil { + p.doParseSpans() + } else if p.ProfileParser != nil { + p.doParseProfile() + } + + return p.res +} + +func (p *parserDoer) doParseProfile() { + parser := p.ProfileParser + + parser.SetOnProfile(p.onProfile) + p.size = 0 + p.resetProfile() + + go func() { + defer p.tamePanic() + err := parser.Decode() + if err != nil { + p.res <- &model.ParserResponse{Error: err} + close(p.res) + return + } + p.res <- &model.ParserResponse{ + ProfileRequest: p.profile, + } + + close(p.res) + }() +} + +func (p *parserDoer) resetProfile() { + p.profile = &model.ProfileData{} +} +func (p *parserDoer) doParseLogs() { + parser := p.LogsParser + meta := "" + _meta := p.ctx.ctx.Value("META") + if _meta != nil { + meta = _meta.(string) + } + + p.ttlDays = 0 + ttlDays := p.ctx.ctx.Value("TTL_DAYS") + if ttlDays != nil { + p.ttlDays = ttlDays.(uint16) + } + + p.tsSpl = newTimeSeriesAndSamples(p.res, meta) + + parser.SetOnEntries(p.onEntries) + p.tsSpl.reset() + + go func() { + defer p.tamePanic() + err := parser.Decode() + if err != nil { + p.res <- &model.ParserResponse{Error: err} + close(p.res) + return + } + p.tsSpl.flush() + p.tsSpl.reset() + close(p.res) + }() +} + +func (p *parserDoer) doParseSpans() { + parser := p.SpansParser + parser.SetOnEntry(p.onSpan) + + p.size = 0 + p.resetSpans() + + go func() { + defer p.tamePanic() + err := parser.Decode() + if err != nil { + p.res <- &model.ParserResponse{Error: err} + close(p.res) + return + } + p.res <- &model.ParserResponse{ + SpansRequest: p.spans, + SpansAttrsRequest: p.attrs, + } + close(p.res) + }() +} + +func (p *parserDoer) tamePanic() { + if err := recover(); err != nil { + logger.Error(err, " stack:", string(debug.Stack())) + p.res <- &model.ParserResponse{Error: fmt.Errorf("panic: %v", err)} + close(p.res) + recover() + } +} +func (p *parserDoer) resetSpans() { + p.spans = &model.TempoSamples{} + p.attrs = &model.TempoTag{} +} + +func (p *parserDoer) onProfile(timestampNs uint64, + Type string, + serviceName string, + samplesTypesUnits []model.StrStr, periodType string, + periodUnit string, tags []model.StrStr, + durationNs uint64, payloadType string, payload []byte, + valuersAgg []model.ValuesAgg, tree []model.TreeRootStructure, functions []model.Function) error { + p.profile.TimestampNs = append(p.profile.TimestampNs, timestampNs) + p.profile.Ptype = append(p.profile.Ptype, Type) + p.profile.ServiceName = append(p.profile.ServiceName, serviceName) + p.profile.PeriodType = append(p.profile.PeriodType, periodType) + p.profile.PeriodUnit = append(p.profile.PeriodUnit, periodUnit) + p.profile.DurationNs = append(p.profile.DurationNs, durationNs) + p.profile.PayloadType = append(p.profile.PayloadType, payloadType) + p.profile.Payload = append(p.profile.Payload, payload) + p.profile.SamplesTypesUnits = samplesTypesUnits + p.profile.Tags = tags + p.profile.ValuesAgg = valuersAgg + p.profile.Function = functions + p.profile.Tree = tree + + p.profile.Size = p.calculateProfileSize() + + if p.profile.Size > 1*1024*1024 { + p.res <- &model.ParserResponse{ + SpansRequest: p.spans, + SpansAttrsRequest: p.attrs, + } + p.resetProfile() + } + //p.res <- &model.ParserResponse{ + // ProfileRequest: p.profile, + //} + + return nil +} +func (p *parserDoer) calculateProfileSize() int { + size := 0 + + // Add sizes for all slices + size += 8 // timestampNs (uint64) + size += len(p.profile.Ptype) + size += len(p.profile.ServiceName) + size += len(p.profile.PeriodType) + size += len(p.profile.PeriodUnit) + size += 8 // durationNs (uint64) + size += len(p.profile.PayloadType) + size += len(p.profile.Payload) + + // Calculate size for slices of struct arrays + for _, st := range p.profile.SamplesTypesUnits { + size += len(st.Str1) + len(st.Str2) + } + for _, tag := range p.profile.Tags { + size += len(tag.Str2) + len(tag.Str1) + } + + // Accumulate the size + return size + +} +func (p *parserDoer) onEntries(labels [][]string, timestampsNS []int64, + message []string, value []float64, types []uint8) error { + + ttlDays := p.ttlDays + if ttlDays == 0 { + var _labels [][]string + for _, lbl := range labels { + if lbl[0] == "__ttl_days__" { + _ttlDays, err := strconv.ParseInt(lbl[1], 10, 16) + if err == nil { + ttlDays = uint16(_ttlDays) + } + continue + } + _labels = append(_labels, lbl) + } + labels = _labels + } + + dates := map[time.Time]bool{} + fp := fingerprintLabels(labels) + + p.tsSpl.spl.MMessage = append(p.tsSpl.spl.MMessage, message...) + p.tsSpl.spl.MValue = append(p.tsSpl.spl.MValue, value...) + p.tsSpl.spl.MTimestampNS = append(p.tsSpl.spl.MTimestampNS, timestampsNS...) + p.tsSpl.spl.MFingerprint = append(p.tsSpl.spl.MFingerprint, fastFillArray(len(timestampsNS), fp)...) + p.tsSpl.spl.MTTLDays = append(p.tsSpl.spl.MTTLDays, fastFillArray(len(timestampsNS), ttlDays)...) + p.tsSpl.spl.MType = append(p.tsSpl.spl.MType, types...) + + var tps [3]bool + for _, t := range types { + tps[t] = true + } + + for i, tsns := range timestampsNS { + dates[time.Unix(tsns/1000000000, 0).Truncate(time.Hour*24)] = true + p.tsSpl.spl.Size += len(message[i]) + 26 + } + + for d := range dates { + if maybeAddFp(d, fp, p.ctx.fpCache) { + _labels := encodeLabels(labels) + for t, _ := range tps { + if !tps[t] { + continue + } + + p.tsSpl.ts.MDate = append(p.tsSpl.ts.MDate, d) + p.tsSpl.ts.MLabels = append(p.tsSpl.ts.MLabels, _labels) + p.tsSpl.ts.MFingerprint = append(p.tsSpl.ts.MFingerprint, fp) + p.tsSpl.ts.MType = append(p.tsSpl.ts.MType, uint8(t)) + p.tsSpl.ts.MTTLDays = append(p.tsSpl.ts.MTTLDays, ttlDays) + p.tsSpl.ts.Size += 14 + len(_labels) + } + } + } + + if p.tsSpl.spl.Size+p.tsSpl.ts.Size > 1*1024*1024 { + p.tsSpl.flush() + p.tsSpl.reset() + } + + return nil +} + +func (p *parserDoer) onSpan(traceId []byte, spanId []byte, timestampNs int64, durationNs int64, + parentId string, name string, serviceName string, payload []byte, key []string, val []string) error { + p.spans.MTraceId = append(p.spans.MTraceId, traceId) + p.spans.MSpanId = append(p.spans.MSpanId, spanId) + p.spans.MTimestampNs = append(p.spans.MTimestampNs, timestampNs) + p.spans.MDurationNs = append(p.spans.MDurationNs, durationNs) + p.spans.MParentId = append(p.spans.MParentId, parentId) + p.spans.MName = append(p.spans.MName, name) + p.spans.MServiceName = append(p.spans.MServiceName, serviceName) + p.spans.MPayloadType = append(p.spans.MPayloadType, p.payloadType) + p.spans.MPayload = append(p.spans.MPayload, payload) + + p.spans.Size += 49 + len(parentId) + len(name) + len(serviceName) + len(payload) + + for i, k := range key { + p.attrs.MTraceId = append(p.attrs.MTraceId, traceId) + p.attrs.MSpanId = append(p.attrs.MSpanId, spanId) + p.attrs.MTimestampNs = append(p.attrs.MTimestampNs, timestampNs) + p.attrs.MDurationNs = append(p.attrs.MDurationNs, durationNs) + p.attrs.MKey = append(p.attrs.MKey, k) + p.attrs.MVal = append(p.attrs.MVal, val[i]) + p.attrs.MDate = append(p.attrs.MDate, time.Unix(timestampNs/1000000000, 0)) + p.attrs.Size += 40 + len(k) + len(val[i]) + } + + if p.attrs.Size+p.spans.Size > 1*1024*1024 { + p.res <- &model.ParserResponse{ + SpansRequest: p.spans, + SpansAttrsRequest: p.attrs, + } + p.resetSpans() + } + + return nil +} + +type buildOption func(builder *parserBuilder) *parserBuilder + +func Build(options ...buildOption) ParsingFunction { + builder := &parserBuilder{} + for _, o := range options { + builder = o(builder) + } + return func(ctx context.Context, body io.Reader, fpCache numbercache.ICache[uint64]) chan *model.ParserResponse { + doer := &parserDoer{ + ctx: &ParserCtx{ + bodyReader: body, + fpCache: fpCache, + ctx: ctx, + ctxMap: map[string]string{}, + }, + PreParse: builder.PreParse, + payloadType: builder.payloadType, + } + if builder.LogsParser != nil { + doer.LogsParser = builder.LogsParser(doer.ctx) + } else if builder.SpansParser != nil { + doer.SpansParser = builder.SpansParser(doer.ctx) + } else { + doer.ProfileParser = builder.ProfileParser(doer.ctx) + } + return doer.Do() + } +} +func withProfileParser(fn func(ctx *ParserCtx) iProfilesParser) buildOption { + return func(builder *parserBuilder) *parserBuilder { + builder.ProfileParser = fn + return builder + } +} +func withLogsParser(fn func(ctx *ParserCtx) iLogsParser) buildOption { + return func(builder *parserBuilder) *parserBuilder { + builder.LogsParser = fn + return builder + } +} + +func withSpansParser(fn func(ctx *ParserCtx) iSpansParser) buildOption { + return func(builder *parserBuilder) *parserBuilder { + builder.SpansParser = fn + return builder + } +} + +func withStringValueFromCtx(key string) buildOption { + return func(builder *parserBuilder) *parserBuilder { + builder.PreParse = append(builder.PreParse, func(ctx *ParserCtx) error { + res := ctx.ctx.Value(key) + if res != nil { + ctx.ctxMap[key] = res.(string) + } + return nil + }) + return builder + } +} + +var withBufferedBody buildOption = func(builder *parserBuilder) *parserBuilder { + builder.PreParse = append(builder.PreParse, func(ctx *ParserCtx) error { + var err error + ctx.bodyBuffer, err = io.ReadAll(ctx.bodyReader) + if err != nil { + return err + } + ctx.bodyReader = nil + return nil + }) + return builder +} + +func withParsedBody(fn func() proto.Message) buildOption { + return func(builder *parserBuilder) *parserBuilder { + builder.PreParse = append(builder.PreParse, func(ctx *ParserCtx) error { + obj := fn() + err := proto.Unmarshal(ctx.bodyBuffer, obj) + if err != nil { + return err + } + ctx.bodyObject = obj + return nil + }) + return builder + } +} + +func withPayloadType(tp int8) buildOption { + return func(builder *parserBuilder) *parserBuilder { + builder.payloadType = tp + return builder + } +} + +func maybeAddFp(date time.Time, fp uint64, fpCache numbercache.ICache[uint64]) bool { + dateTS := date.Unix() + var bs [16]byte + copy(bs[0:8], unsafe.Slice((*byte)(unsafe.Pointer(&dateTS)), 16)) + copy(bs[8:16], unsafe.Slice((*byte)(unsafe.Pointer(&fp)), 16)) + _fp := city.CH64(bs[:]) + return !fpCache.CheckAndSet(_fp) +} diff --git a/writer/utils/unmarshal/datadogCFJsonUnmarshal.go b/writer/utils/unmarshal/datadogCFJsonUnmarshal.go new file mode 100644 index 00000000..43b5b118 --- /dev/null +++ b/writer/utils/unmarshal/datadogCFJsonUnmarshal.go @@ -0,0 +1,146 @@ +package unmarshal + +import ( + "bufio" + "fmt" + "github.com/go-faster/jx" + "github.com/metrico/qryn/writer/model" + customErrors "github.com/metrico/qryn/writer/utils/errors" + "time" +) + +type datadogCFRequestDec struct { + ctx *ParserCtx + + DDSource string + ScriptName string + Outcome string + EventType string + TsNs int64 + ActionResult *bool + ActionType string + ActorType string + ResourceType string + + onEntries onEntriesHandler +} + +func (d *datadogCFRequestDec) Decode() error { + scanner := bufio.NewScanner(d.ctx.bodyReader) + scanner.Split(bufio.ScanLines) + + d.DDSource = d.ctx.ctxMap["ddsource"] + for scanner.Scan() { + bytes := scanner.Bytes() + err := d.DecodeLine(bytes) + if err != nil { + return customErrors.NewUnmarshalError(err) + } + t := time.Now() + if d.TsNs != 0 { + t = time.Unix(d.TsNs/1000000000, d.TsNs%1000000000) + } + err = d.onEntries(d.GetLabels(), []int64{t.UnixNano()}, []string{scanner.Text()}, []float64{0}, + []uint8{model.SAMPLE_TYPE_LOG}) + if err != nil { + return err + } + } + return nil +} + +func (d *datadogCFRequestDec) SetOnEntries(h onEntriesHandler) { + d.onEntries = h +} + +func (d *datadogCFRequestDec) DecodeLine(line []byte) error { + d.ScriptName = "" + d.Outcome = "" + d.EventType = "" + d.TsNs = 0 + d.ActionResult = nil + d.ActionType = "" + d.ActorType = "" + d.ResourceType = "" + dec := jx.DecodeBytes(line) + return dec.Obj(d.decodeRootObj) + +} + +func (d *datadogCFRequestDec) decodeRootObj(dec *jx.Decoder, key string) error { + var err error + switch key { + case "EventType": + d.EventType, err = dec.Str() + return err + case "Outcome": + d.Outcome, err = dec.Str() + return err + case "ScriptName": + d.ScriptName, err = dec.Str() + return err + case "EventTimestampMs": + tp := dec.Next() + switch tp { + case jx.Number: + d.TsNs, err = dec.Int64() + d.TsNs *= 1000000 + return err + } + case "When": + tp := dec.Next() + switch tp { + case jx.Number: + d.TsNs, err = dec.Int64() + return err + } + case "ActionResult": + tp := dec.Next() + switch tp { + case jx.Bool: + actRes := false + actRes, err = dec.Bool() + d.ActionResult = &actRes + } + return err + case "ActionType": + d.ActionType, err = dec.Str() + return err + case "ActorType": + d.ActorType, err = dec.Str() + return err + case "ResourceType": + d.ResourceType, err = dec.Str() + return err + } + return dec.Skip() +} + +func (d *datadogCFRequestDec) GetLabels() [][]string { + strActResult := "" + if d.ActionResult != nil { + strActResult = fmt.Sprintf("%v", *d.ActionResult) + } + var labels [][]string + for _, label := range [][]string{ + {"ddsource", d.DDSource}, + {"ScriptName", d.ScriptName}, + {"Outcome", d.Outcome}, + {"EventType", d.EventType}, + {"ActionResult", strActResult}, + {"ActionType", d.ActionType}, + {"ActorType", d.ActorType}, + {"ResourceType", d.ResourceType}, + } { + if label[1] != "" { + labels = append(labels, label) + } + } + return labels +} + +var UnmarshallDatadogCFJSONV2 = Build( + withStringValueFromCtx("ddsource"), + withLogsParser(func(ctx *ParserCtx) iLogsParser { + return &datadogCFRequestDec{ctx: ctx} + })) diff --git a/writer/utils/unmarshal/datadogJsonUnmarshal.go b/writer/utils/unmarshal/datadogJsonUnmarshal.go new file mode 100644 index 00000000..7a4ee7e1 --- /dev/null +++ b/writer/utils/unmarshal/datadogJsonUnmarshal.go @@ -0,0 +1,100 @@ +package unmarshal + +import ( + "github.com/go-faster/jx" + "github.com/metrico/qryn/writer/model" + customErrors "github.com/metrico/qryn/writer/utils/errors" + "regexp" + "time" +) + +type datadogRequestDec struct { + ctx *ParserCtx + + Source string + Tags [][]string + Hostname string + Message string + Service string + TsMs int64 + SourceType string + + onEntries onEntriesHandler +} + +var tagPattern = regexp.MustCompile(`([\p{L}][\p{L}_0-9\-.\\/]*):([\p{L}_0-9\-.\\/:]+)(,|$)`) + +func (d *datadogRequestDec) SetOnEntries(h onEntriesHandler) { + d.onEntries = h +} + +func (d *datadogRequestDec) Decode() error { + dec := jx.Decode(d.ctx.bodyReader, 64*1024) + return dec.Arr(func(dec *jx.Decoder) error { + d.Source = "" + d.Tags = d.Tags[:0] + d.Hostname = "" + d.Message = "" + d.Service = "" + d.TsMs = 0 + return d.DecodeEntry(dec) + }) +} + +func (d *datadogRequestDec) DecodeEntry(dec *jx.Decoder) error { + err := dec.Obj(func(dec *jx.Decoder, key string) error { + var err error + switch key { + case "ddsource": + d.Source, err = dec.Str() + case "ddtags": + val, err := dec.Str() + if err != nil { + return customErrors.NewUnmarshalError(err) + } + for _, match := range tagPattern.FindAllStringSubmatch(val, -1) { + d.Tags = append(d.Tags, []string{match[1], match[2]}) + } + case "hostname": + d.Hostname, err = dec.Str() + case "message": + d.Message, err = dec.Str() + case "service": + d.Service, err = dec.Str() + case "timestamp": + d.TsMs, err = dec.Int64() + case "source_type": + d.SourceType, err = dec.Str() + default: + err = dec.Skip() + } + return err + }) + if err != nil { + return customErrors.NewUnmarshalError(err) + } + + for _, l := range [][]string{ + {"ddsource", d.Source}, + {"service", d.Service}, + {"hostname", d.Hostname}, + {"source_type", d.SourceType}, + {"type", "datadog"}, + } { + if l[1] != "" { + d.Tags = append(d.Tags, l) + } + } + + t := time.Now() + if d.TsMs != 0 { + t = time.Unix(d.TsMs/1000, d.TsMs%1000*1000000) + } + return d.onEntries(d.Tags, []int64{t.UnixNano()}, []string{d.Message}, []float64{0}, + []uint8{model.SAMPLE_TYPE_LOG}) +} + +var UnmarshallDatadogV2JSONV2 = Build( + withLogsParser(func(ctx *ParserCtx) iLogsParser { + return &datadogRequestDec{ctx: ctx} + })) diff --git a/writer/utils/unmarshal/datadogMetricsJsonUnmarshal.go b/writer/utils/unmarshal/datadogMetricsJsonUnmarshal.go new file mode 100644 index 00000000..60341b82 --- /dev/null +++ b/writer/utils/unmarshal/datadogMetricsJsonUnmarshal.go @@ -0,0 +1,167 @@ +package unmarshal + +import ( + "fmt" + "github.com/go-faster/jx" + "github.com/metrico/qryn/writer/model" + custom_errors "github.com/metrico/qryn/writer/utils/errors" + "strings" + "time" +) + +type point struct { + tsNs int64 + value float64 +} + +type datadogMetricsRequestDec struct { + ctx *ParserCtx + + Labels [][]string + tsNs []int64 + values []float64 + + path []interface{} + + onEntries onEntriesHandler +} + +func (d *datadogMetricsRequestDec) Decode() error { + dec := jx.Decode(d.ctx.bodyReader, 64*1024) + return d.WrapError(dec.Obj(func(dec *jx.Decoder, key string) error { + switch key { + case "series": + d.path = append(d.path, "series") + return d.WrapError(dec.Arr(func(dec *jx.Decoder) error { + d.Labels = d.Labels[:0] + d.tsNs = d.tsNs[:0] + d.values = d.values[:0] + err := d.WrapError(dec.Obj(d.DecodeSeriesItem)) + if err != nil { + return err + } + return d.WrapError(d.onEntries(d.Labels, d.tsNs, make([]string, len(d.values)), d.values, + fastFillArray[uint8](len(d.values), model.SAMPLE_TYPE_METRIC))) + })) + } + return d.WrapError(dec.Skip()) + })) +} + +func (d *datadogMetricsRequestDec) SetOnEntries(h onEntriesHandler) { + d.onEntries = h +} + +func (d *datadogMetricsRequestDec) DecodeSeriesItem(dec *jx.Decoder, key string) error { + switch key { + case "metric": + d.path = append(d.path, "series") + val, err := d.MaybeString(dec) + d.Labels = append(d.Labels, []string{"__name__", val}) + d.path = d.path[:len(d.path)-1] + return d.WrapError(err) + case "resources": + d.path = append(d.path, "resources") + i := -1 + d.path = append(d.path, &i) + err := d.WrapError(d.MaybeArr(dec, func(dec *jx.Decoder) error { + i++ + return d.WrapError(d.MaybeObj(dec, func(dec *jx.Decoder, key string) error { + d.path = append(d.path, key) + val, err := d.MaybeString(dec) + d.Labels = append(d.Labels, []string{fmt.Sprintf("resource%d_%s", i+1, key), val}) + d.path = d.path[:len(d.path)-1] + return d.WrapError(err) + })) + })) + d.path = d.path[:len(d.path)-2] + return d.WrapError(err) + case "points": + d.path = append(d.path, "points") + tsNs := time.Now().UnixNano() + val := float64(0) + i := -1 + d.path = append(d.path, &i) + err := d.WrapError(dec.Arr(func(dec *jx.Decoder) error { + i++ + err := d.WrapError(dec.Obj(func(dec *jx.Decoder, key string) error { + var err error + switch key { + case "timestamp": + d.path = append(d.path, "timestamp") + tsNs, err = dec.Int64() + err = d.WrapError(err) + tsNs *= 1000000000 + d.path = d.path[:len(d.path)-1] + return d.WrapError(err) + case "value": + d.path = append(d.path, "timestamp") + val, err = dec.Float64() + err = d.WrapError(err) + d.path = d.path[:len(d.path)-1] + return d.WrapError(err) + } + return d.WrapError(dec.Skip()) + })) + d.tsNs = append(d.tsNs, tsNs) + d.values = append(d.values, val) + return d.WrapError(err) + })) + d.path = d.path[:len(d.path)-2] + return d.WrapError(err) + } + return d.WrapError(dec.Skip()) +} + +func (d *datadogMetricsRequestDec) MaybeString(dec *jx.Decoder) (string, error) { + tp := dec.Next() + switch tp { + case jx.String: + res, err := dec.Str() + return res, d.WrapError(err) + } + return "", nil +} +func (d *datadogMetricsRequestDec) MaybeObj(dec *jx.Decoder, f func(d *jx.Decoder, key string) error) error { + tp := dec.Next() + switch tp { + case jx.Object: + return d.WrapError(dec.Obj(f)) + } + return nil +} + +func (d *datadogMetricsRequestDec) MaybeArr(dec *jx.Decoder, f func(d *jx.Decoder) error) error { + tp := dec.Next() + switch tp { + case jx.Array: + return d.WrapError(dec.Arr(f)) + } + return nil +} + +func (d *datadogMetricsRequestDec) WrapError(err error) error { + if err == nil { + return nil + } + if strings.HasPrefix(err.Error(), "json error") { + return custom_errors.NewUnmarshalError(err) + //return err + } + path := "" + for _, i := range d.path { + switch i.(type) { + case string: + path += "." + i.(string) + case *int: + path += "." + fmt.Sprintf("%d", *(i.(*int))) + } + } + + return custom_errors.NewUnmarshalError(fmt.Errorf("json error path: %s; error: %s", path, err.Error())) +} + +var UnmarshallDatadogMetricsV2JSONV2 = Build( + withLogsParser(func(ctx *ParserCtx) iLogsParser { + return &datadogMetricsRequestDec{ctx: ctx} + })) diff --git a/writer/utils/unmarshal/elasticUnmarshal.go b/writer/utils/unmarshal/elasticUnmarshal.go new file mode 100644 index 00000000..4fed530d --- /dev/null +++ b/writer/utils/unmarshal/elasticUnmarshal.go @@ -0,0 +1,142 @@ +package unmarshal + +import ( + "bufio" + "github.com/go-faster/jx" + "github.com/metrico/qryn/writer/model" + customErrors "github.com/metrico/qryn/writer/utils/errors" + "github.com/metrico/qryn/writer/utils/numbercache" + "io" + "time" +) + +type ElasticUnmarshalOpts struct { + DB string + Body []byte + BodyStream io.Reader + Target string + ID string + FPCache numbercache.ICache[uint64] +} + +type ElasticUnmarshal struct { + ctx *ParserCtx + onEntries onEntriesHandler +} + +func (e *ElasticUnmarshal) Decode() error { + labels := [][]string{{"type", "elastic"}, {"_index", e.ctx.ctxMap["target"]}} + if id, ok := e.ctx.ctxMap["id"]; ok { + labels = append(labels, []string{"_id", id}) + } + return e.onEntries(labels, []int64{time.Now().UnixNano()}, []string{string(e.ctx.bodyBuffer)}, []float64{0}, + []uint8{model.SAMPLE_TYPE_LOG}) +} +func (e *ElasticUnmarshal) SetOnEntries(h onEntriesHandler) { + e.onEntries = h +} + +var ElasticDocUnmarshalV2 = Build( + withStringValueFromCtx("target"), + withStringValueFromCtx("id"), + withBufferedBody, + withLogsParser(func(ctx *ParserCtx) iLogsParser { + return &ElasticUnmarshal{ctx: ctx} + })) + +type elasticBulkDec struct { + ctx *ParserCtx + onEntries onEntriesHandler + + labels [][]string +} + +func (e *elasticBulkDec) Decode() error { + scanner := bufio.NewScanner(e.ctx.bodyReader) + for scanner.Scan() { + err := e.decodeLine(scanner.Bytes()) + if err != nil { + return customErrors.NewUnmarshalError(err) + } + } + return nil +} + +func (e *elasticBulkDec) SetOnEntries(h onEntriesHandler) { + e.onEntries = h +} + +func (e *elasticBulkDec) decodeLine(line []byte) error { + dec := jx.DecodeBytes(line) + noContent := false + // Check if the line is empty or not a valid JSON + if len(line) == 0 { + return nil + } + err := dec.Obj(func(d *jx.Decoder, key string) error { + if noContent { + return dec.Skip() + } + switch key { + case "delete": + noContent = true + e.labels = e.labels[:0] + // Skip remaining content for delete operation + return d.Skip() + case "update": + noContent = true + e.labels = e.labels[:0] + // Skip remaining content for update operation + return d.Skip() + case "index", "create": + noContent = true + return e.decodeCreateObj(d) + default: + // Handle unexpected keys + return d.Skip() + } + }) + + if err != nil { + // return fmt.Errorf("error decoding line: %w", err) + return customErrors.NewUnmarshalError(err) + } + + // Ensure `e.labels` is processed correctly + if noContent || len(e.labels) == 0 { + return nil + } + + // Invoke onEntries with the processed data + return e.onEntries(e.labels, []int64{time.Now().UnixNano()}, []string{string(line)}, []float64{0}, + []uint8{model.SAMPLE_TYPE_LOG}) +} + +func (e *elasticBulkDec) decodeCreateObj(dec *jx.Decoder) error { + target := e.ctx.ctxMap["target"] + e.labels = [][]string{{"type", "elastic"}} + if target != "" { + e.labels = append(e.labels, []string{"_index", target}) + } + return dec.Obj(func(d *jx.Decoder, key string) error { + tp := d.Next() + if tp != jx.String { + return d.Skip() + } + if (target != "" && key == "_index") || key == "type" { + return d.Skip() + } + val, err := dec.Str() + if err != nil { + return customErrors.NewUnmarshalError(err) + } + e.labels = append(e.labels, []string{key, val}) + return nil + }) +} + +var ElasticBulkUnmarshalV2 = Build( + withStringValueFromCtx("target"), + withLogsParser(func(ctx *ParserCtx) iLogsParser { + return &elasticBulkDec{ctx: ctx} + })) diff --git a/writer/utils/unmarshal/golangPprof.go b/writer/utils/unmarshal/golangPprof.go new file mode 100644 index 00000000..ccf58ed2 --- /dev/null +++ b/writer/utils/unmarshal/golangPprof.go @@ -0,0 +1,490 @@ +package unmarshal + +import ( + "bytes" + "compress/gzip" + "encoding/binary" + "fmt" + "github.com/go-faster/city" + pprof_proto "github.com/google/pprof/profile" + "github.com/metrico/qryn/writer/model" + "io" + "io/ioutil" + "mime/multipart" + "regexp" + "sort" + "strconv" + "strings" + "sync" +) + +const ( + ingestPath = "/ingest" + + formatJfr = "jfr" + formatPprof = "profile" + filePprof = "profile.pprof" +) + +type SampleType struct { + Key string + Sum int64 + Count int32 +} +type Label struct { + Name, Value string +} + +type PayloadType uint8 + +// Represents the high-level type of a profile +type ProfileType struct { + Type string + PeriodType string + PeriodUnit string + SampleType []string + SampleUnit []string +} +type ProfileIR struct { + Type ProfileType + DurationNano int64 + TimeStampNao int64 + Payload *bytes.Buffer + PayloadType PayloadType + ValueAggregation interface{} + Profile *pprof_proto.Profile +} + +type profTrieNode struct { + parentId uint64 + funcId uint64 + nodeId uint64 + values []profTrieValue +} + +type profTrieValue struct { + name string + self int64 + total int64 +} +type codec uint8 + +const ( + Gzip codec = iota +) + +// Decodes compressed streams +type Decompressor struct { + maxUncompressedSizeBytes int64 + decoders map[codec]func(body io.Reader) (io.Reader, error) +} + +type pProfProtoDec struct { + ctx *ParserCtx + onProfiles onProfileHandler + uncompressedBufPool *sync.Pool + decompressor *Decompressor +} + +func (p *pProfProtoDec) Decode() error { + var timestampNs uint64 + var durationNs uint64 + var tags []model.StrStr + fromValue := p.ctx.ctxMap["from"] + start, err := strconv.ParseUint(fromValue, 10, 64) + if err != nil { + fmt.Println("st error", err.Error()) + return fmt.Errorf("failed to parse start time: %w", err) + } + + endValue := p.ctx.ctxMap["until"] + end, err := strconv.ParseUint(endValue, 10, 64) + if err != nil { + fmt.Errorf("failed to parse end time: %w", err) + } + name := p.ctx.ctxMap["name"] + i := strings.Index(name, "{") + length := len(name) + if i < 0 { + i = length + } else { + + promqllike := name[i+1 : length-1] // stripe {} + if len(promqllike) > 0 { + words := strings.FieldsFunc(promqllike, func(r rune) bool { return r == '=' || r == ',' }) + sz := len(words) + if sz == 0 || sz%2 != 0 { + return fmt.Errorf("failed to compile labels") + } + + for j := 0; j < len(words); j += 2 { + tags = append(tags, model.StrStr{ + Str1: words[j], + Str2: words[j+1], + }) + } + } + } + name = name[:i] + durationNs = end - start + timestampNs = start + + buf := acquireBuf(p.uncompressedBufPool) + defer func() { + releaseBuf(p.uncompressedBufPool, buf) + }() + + data, err := ioutil.ReadAll(p.ctx.bodyReader) + if err != nil { + fmt.Println("Error reading from reader:", err) + return err + } + f, err := processMIMEData(string(data)) + if err != nil { + fmt.Println("Error reading from reader:", err) + return err + } + // Convert bytes to string + err = p.decompressor.Decompress(f, Gzip, buf) + if err != nil { + return fmt.Errorf("failed to decompress body: %w", err) + } + + ps, err := Parse(buf) + if err != nil { + return fmt.Errorf("failed to parse pprof: %w", err) + } + + for _, profile := range ps { + + var sampleUnitArray []model.StrStr + var functionArray []model.Function + var treeArray []model.TreeRootStructure + var ValuesAgg []model.ValuesAgg + functions, tree := postProcessProf(profile.Profile) + valueAgg := profile.ValueAggregation.([]SampleType) + + for i, sType := range profile.Type.SampleType { + sampleUnitArray = append(sampleUnitArray, model.StrStr{ + Str1: sType, + Str2: profile.Type.SampleUnit[i], + }) + } + + for _, v := range valueAgg { + ValuesAgg = append(ValuesAgg, model.ValuesAgg{ + ValueStr: v.Key, + ValueInt64: v.Sum, + ValueInt32: v.Count, + }) + } + + for _, f := range functions { + function := model.Function{ + ValueInt64: f.ValueInt64, + ValueStr: f.ValueStr, + } + functionArray = append(functionArray, function) + } + + for _, t := range tree { + var valuesArr []model.ValuesArrTuple + for _, v := range t.values { + valuesArr = append(valuesArr, model.ValuesArrTuple{ + ValueStr: v.name, + FirstValueInt64: v.self, + SecondValueInt64: v.total, + }) + } + treeStruct := model.TreeRootStructure{ + Field1: t.parentId, + Field2: t.funcId, + Field3: t.nodeId, + ValueArrTuple: valuesArr, + } + + treeArray = append(treeArray, treeStruct) + + } + payload := profile.Payload.Bytes() + payloadType := fmt.Sprint(profile.PayloadType) + err = p.onProfiles(timestampNs, profile.Type.Type, + name, + sampleUnitArray, + profile.Type.PeriodType, + profile.Type.PeriodUnit, + tags, durationNs, payloadType, payload, ValuesAgg, treeArray, functionArray) + if err != nil { + + fmt.Println("Error at onProfiles") + return err + } + } + + return nil +} + +func (p *pProfProtoDec) SetOnProfile(h onProfileHandler) { + p.onProfiles = h + p.uncompressedBufPool = &sync.Pool{} + p.decompressor = NewDecompressor(100000) +} + +var UnmarshalProfileProtoV2 = Build( + withStringValueFromCtx("from"), + withStringValueFromCtx("name"), + withStringValueFromCtx("until"), + withProfileParser(func(ctx *ParserCtx) iProfilesParser { + return &pProfProtoDec{ctx: ctx} + })) + +func acquireBuf(p *sync.Pool) *bytes.Buffer { + v := p.Get() + if v == nil { + return new(bytes.Buffer) + } + return v.(*bytes.Buffer) +} +func releaseBuf(p *sync.Pool, buf *bytes.Buffer) { + buf.Reset() + p.Put(buf) +} + +// Decodes the accepted reader, applying the configured size limit to avoid oom by compression bomb +func (d *Decompressor) Decompress(r io.Reader, c codec, out *bytes.Buffer) error { + decoder, ok := d.decoders[c] + if !ok { + return fmt.Errorf("unsupported encoding") + } + + dr, err := decoder(r) + if err != nil { + fmt.Println("error during decode........") + return err + } + + return d.readBytes(dr, out) +} + +func (d *Decompressor) readBytes(r io.Reader, out *bytes.Buffer) error { + // read max+1 to validate size via a single Read() + lr := io.LimitReader(r, d.maxUncompressedSizeBytes+1) + + n, err := out.ReadFrom(lr) + if err != nil { + return err + } + if n < 1 { + return fmt.Errorf("empty profile") + } + if n > d.maxUncompressedSizeBytes { + return fmt.Errorf("body size exceeds the limit %d bytes", d.maxUncompressedSizeBytes) + } + return nil +} + +func Parse(data *bytes.Buffer) ([]ProfileIR, error) { + // Parse pprof data + pProfData, err := pprof_proto.Parse(data) + if err != nil { + return nil, err + } + + // Process pprof data and create SampleType slice + var sampleTypes []string + var sampleUnits []string + var valueAggregates []SampleType + + for i, st := range pProfData.SampleType { + sampleTypes = append(sampleTypes, pProfData.SampleType[i].Type) + sampleUnits = append(sampleUnits, pProfData.SampleType[i].Unit) + sum, count := calculateSumAndCount(pProfData, i) + valueAggregates = append(valueAggregates, SampleType{fmt.Sprintf("%s:%s", st.Type, st.Unit), sum, count}) + } + + var profiles []ProfileIR + var profileType string + + switch pProfData.PeriodType.Type { + case "cpu": + profileType = "process_cpu" + case "wall": + profileType = "wall" + case "mutex", "contentions": + profileType = "mutex" + case "goroutine": + profileType = "goroutines" + case "objects", "space", "alloc", "inuse": + profileType = "memory" + case "block": + profileType = "block" + } + + profileTypeInfo := ProfileType{ + PeriodType: pProfData.PeriodType.Type, + PeriodUnit: pProfData.PeriodType.Unit, + SampleType: sampleTypes, + SampleUnit: sampleUnits, + Type: profileType, + } + + // Create a new ProfileIR instance + profile := ProfileIR{ + ValueAggregation: valueAggregates, + Type: profileTypeInfo, + Profile: pProfData, + } + profile.Payload = new(bytes.Buffer) + pProfData.WriteUncompressed(profile.Payload) + // Append the profile to the result + profiles = append(profiles, profile) + return profiles, nil +} + +func calculateSumAndCount(samples *pprof_proto.Profile, sampleTypeIndex int) (int64, int32) { + var sum int64 + count := int32(len(samples.Sample)) + for _, sample := range samples.Sample { + // Check if the sample has a value for the specified sample type + if sampleTypeIndex < len(sample.Value) { + // Accumulate the value for the specified sample type + sum += sample.Value[sampleTypeIndex] + } + } + + return sum, count +} + +func postProcessProf(profile *pprof_proto.Profile) ([]*model.Function, []*profTrieNode) { + funcs := map[uint64]string{} + tree := map[uint64]*profTrieNode{} + _values := make([]profTrieValue, len(profile.SampleType)) + for i, name := range profile.SampleType { + _values[i] = profTrieValue{ + name: fmt.Sprintf("%s:%s", name.Type, name.Unit), + } + } + for _, sample := range profile.Sample { + parentId := uint64(0) + for i := len(sample.Location) - 1; i >= 0; i-- { + loc := sample.Location[i] + name := "n/a" + if len(loc.Line) > 0 { + name = loc.Line[0].Function.Name + } + fnId := city.CH64([]byte(name)) + funcs[fnId] = name + nodeId := getNodeId(parentId, fnId, len(sample.Location)-i) + node := tree[nodeId] + if node == nil { + values := make([]profTrieValue, len(profile.SampleType)) + copy(values, _values) + node = &profTrieNode{ + parentId: parentId, + funcId: fnId, + nodeId: nodeId, + values: values, + } + + tree[nodeId] = node + } + for j := range node.values { + node.values[j].total += sample.Value[j] + if i == 0 { + node.values[j].self += sample.Value[j] + } + } + parentId = nodeId + } + } + var bFnMap []byte + bFnMap = binary.AppendVarint(bFnMap, int64(len(funcs))) + indices := make([]uint64, 0, len(funcs)) + for fnId := range funcs { + indices = append(indices, fnId) + } + sort.Slice(indices, func(i, j int) bool { return indices[i] > indices[j] }) + var funRes []*model.Function + for _, fnId := range indices { + funRes = append(funRes, &model.Function{ + ValueInt64: fnId, + ValueStr: funcs[fnId], + }) + //bFnMap = binary.AppendUvarint(bFnMap, fnId) + //bFnMap = binary.AppendVarint(bFnMap, int64(len(funcs[fnId]))) + //bFnMap = append(bFnMap, funcs[fnId]...) + } + var bNodeMap []byte + bNodeMap = binary.AppendVarint(bNodeMap, int64(len(tree))) + indices = indices[:0] + for tId := range tree { + indices = append(indices, tId) + } + sort.Slice(indices, func(i, j int) bool { return indices[i] > indices[j] }) + var tressRes []*profTrieNode + for _, id := range indices { + node := tree[id] + tressRes = append(tressRes, node) + + } + return funRes, tressRes +} +func getNodeId(parentId uint64, funcId uint64, traceLevel int) uint64 { + buf := make([]byte, 16) + binary.LittleEndian.PutUint64(buf[0:8], parentId) + binary.LittleEndian.PutUint64(buf[8:16], funcId) + if traceLevel > 511 { + traceLevel = 511 + } + return city.CH64(buf)>>9 | (uint64(traceLevel) << 55) +} + +func NewDecompressor(maxUncompressedSizeBytes int64) *Decompressor { + return &Decompressor{ + maxUncompressedSizeBytes: maxUncompressedSizeBytes, + decoders: map[codec]func(r io.Reader) (io.Reader, error){ + Gzip: func(r io.Reader) (io.Reader, error) { + gr, err := gzip.NewReader(r) + if err != nil { + return nil, err + } + return gr, nil + }, + }, + } +} + +func processMIMEData(data string) (multipart.File, error) { + boundary, err := findBoundary(data) + if err != nil { + return nil, err + } + //buf := new(bytes.Buffer) + + reader := multipart.NewReader(strings.NewReader(data), boundary) + form, err := reader.ReadForm(10 * 1024 * 1024 * 1024) + if err != nil { + return nil, err + } + //var part []*multipart.FileHeader + part, exists := form.File["profile"] + if !exists || len(part) == 0 { + return nil, fmt.Errorf("no file found for 'profile' field") + } + fh := part[0] + f, err := fh.Open() + if err != nil { + return nil, err + } + + return f, nil +} + +func findBoundary(data string) (string, error) { + boundaryRegex := regexp.MustCompile(`(?m)^--([A-Za-z0-9'-]+)\r?\n`) + matches := boundaryRegex.FindStringSubmatch(data) + if len(matches) > 1 { + return matches[1], nil + } + return "", fmt.Errorf("boundary not found") +} diff --git a/writer/utils/unmarshal/influxUnmarshal.go b/writer/utils/unmarshal/influxUnmarshal.go new file mode 100644 index 00000000..c16f0dc6 --- /dev/null +++ b/writer/utils/unmarshal/influxUnmarshal.go @@ -0,0 +1,110 @@ +package unmarshal + +import ( + "bytes" + "github.com/go-logfmt/logfmt" + "github.com/influxdata/telegraf/plugins/parsers/influx" + "github.com/metrico/qryn/writer/model" + customErrors "github.com/metrico/qryn/writer/utils/errors" + "regexp" + "time" +) + +func getMessage(fields map[string]any) (string, error) { + if len(fields) == 1 { + return fields["message"].(string), nil + } + buf := bytes.NewBuffer(make([]byte, 0, 1000)) + encoder := logfmt.NewEncoder(buf) + err := encoder.EncodeKeyvals("message", fields["message"]) + if err != nil { + return "", customErrors.NewUnmarshalError(err) + } + for k, v := range fields { + if k == "message" { + continue + } + err := encoder.EncodeKeyvals(k, v) + if err != nil { + return "", customErrors.NewUnmarshalError(err) + } + } + return buf.String(), nil +} + +type influxDec struct { + ctx *ParserCtx + onEntries onEntriesHandler +} + +func (e *influxDec) Decode() error { + parser := influx.NewStreamParser(e.ctx.bodyReader) + precision := e.ctx.ctx.Value("precision").(time.Duration) + parser.SetTimePrecision(precision) + + for mtr, err := parser.Next(); true; mtr, err = parser.Next() { + if err == influx.EOF { + return nil + } + if err != nil { + return customErrors.NewUnmarshalError(err) + } + labels := [][]string{{"measurement", mtr.Name()}} + for k, v := range mtr.Tags() { + labels = append(labels, []string{k, v}) + } + labels = sanitizeLabels(labels) + + fields := mtr.Fields() + + if _, ok := fields["message"]; ok { + message, err := getMessage(fields) + if err != nil { + return err + } + err = e.onEntries(labels, []int64{mtr.Time().UnixNano()}, []string{message}, []float64{0}, + []uint8{model.SAMPLE_TYPE_LOG}) + if err != nil { + return err + } + continue + } + + labels = append(labels, []string{"__name__", ""}) + nameIdx := len(labels) - 1 + + for k, v := range fields { + var fVal float64 + switch v.(type) { + case int64: + fVal = float64(v.(int64)) + case float64: + fVal = v.(float64) + default: + continue + } + labels[nameIdx][1] = sanitizeMetricName(k) + err = e.onEntries(labels, []int64{mtr.Time().UnixNano()}, []string{""}, []float64{fVal}, + []uint8{model.SAMPLE_TYPE_METRIC}) + if err != nil { + return err + } + } + } + return nil +} + +var metricNameSanitizer = regexp.MustCompile("(^[^a-zA-Z_]|[^a-zA-Z0-9_])") + +func sanitizeMetricName(metricName string) string { + return metricNameSanitizer.ReplaceAllString(metricName, "_") +} + +func (e *influxDec) SetOnEntries(h onEntriesHandler) { + e.onEntries = h +} + +var UnmarshalInfluxDBLogsV2 = Build( + withLogsParser(func(ctx *ParserCtx) iLogsParser { + return &influxDec{ctx: ctx} + })) diff --git a/writer/utils/unmarshal/influx_test.go b/writer/utils/unmarshal/influx_test.go new file mode 100644 index 00000000..f069c7b8 --- /dev/null +++ b/writer/utils/unmarshal/influx_test.go @@ -0,0 +1,57 @@ +package unmarshal + +import ( + "fmt" + "github.com/go-faster/jx" + "regexp" + "strings" + "testing" +) + +const LEN = 64 + +func TestDDTags(t *testing.T) { + var tagPattern = regexp.MustCompile(`([\p{L}][\p{L}_0-9\-.\\/]*):([\p{L}_0-9\-.\\/:]+)(,|$)`) + for _, match := range tagPattern.FindAllStringSubmatch("env:staging,version:5.1,", -1) { + println(match[1], match[2]) + } +} + +func TestAppend(t *testing.T) { + a := make([]string, 0, 10) + b := append(a, "a") + fmt.Println(b[0]) + a = a[:1] + fmt.Println(a[0]) +} + +func BenchmarkFastAppend(b *testing.B) { + for i := 0; i < b.N; i++ { + var res []byte + res = append(res, fastFillArray(LEN, byte(1))...) + } +} + +func BenchmarkAppend(b *testing.B) { + for i := 0; i < b.N; i++ { + var res []byte + for c := 0; c < LEN; c++ { + res = append(res, 1) + } + } +} + +func BenchmarkAppendFill(b *testing.B) { + a := make([]byte, 0, LEN) + for i := 0; i < b.N; i++ { + for c := 0; c < LEN; c++ { + a = append(a, 5) + } + } +} + +func TestJsonError(t *testing.T) { + r := jx.Decode(strings.NewReader(`123`), 1024) + fmt.Println(r.BigInt()) + //fmt.Println(r.Str()) +} diff --git a/writer/utils/unmarshal/legacy/unmarshal.go b/writer/utils/unmarshal/legacy/unmarshal.go new file mode 100644 index 00000000..ffd6325d --- /dev/null +++ b/writer/utils/unmarshal/legacy/unmarshal.go @@ -0,0 +1,31 @@ +package unmarshal + +import ( + "io" + + json "github.com/json-iterator/go" + + "github.com/metrico/qryn/writer/model" +) + +// DecodePushRequest directly decodes json to a logproto.PushRequest +func DecodePushRequest(b io.Reader, r *model.PushRequest) error { + return json.NewDecoder(b).Decode(r) +} + +// DecodePushRequest directly decodes json to a logproto.PushRequest +func DecodePushRequestString(body []byte) (model.PushRequest, error) { + + request := model.PushRequest{} + + if err := json.Unmarshal(body, &request); err != nil { + return request, err + } + + /*if err := json.Unmarshal(body, r); err != nil { + return err + } + */ + + return request, nil +} diff --git a/writer/utils/unmarshal/logsProtobuf.go b/writer/utils/unmarshal/logsProtobuf.go new file mode 100644 index 00000000..9c3c11f8 --- /dev/null +++ b/writer/utils/unmarshal/logsProtobuf.go @@ -0,0 +1,50 @@ +package unmarshal + +import ( + "github.com/metrico/qryn/writer/model" + "github.com/metrico/qryn/writer/utils/proto/logproto" + "google.golang.org/protobuf/proto" +) + +type logsProtoDec struct { + ctx *ParserCtx + onEntries onEntriesHandler +} + +func (l *logsProtoDec) Decode() error { + obj := l.ctx.bodyObject.(*logproto.PushRequest) + var err error + labels := make([][]string, 0, 10) + for _, stream := range obj.GetStreams() { + labels = labels[:0] + labels, err = parseLabelsLokiFormat([]byte(stream.GetLabels()), labels) + if err != nil { + return err + } + labels = sanitizeLabels(labels) + tsns := make([]int64, len(stream.GetEntries())) + msgs := make([]string, len(stream.GetEntries())) + + for i, e := range stream.GetEntries() { + tsns[i] = e.Timestamp.GetSeconds()*1000000000 + int64(e.Timestamp.GetNanos()) + msgs[i] = e.GetLine() + } + err = l.onEntries(labels, tsns, msgs, make([]float64, len(stream.GetEntries())), + fastFillArray[uint8](len(stream.GetEntries()), model.SAMPLE_TYPE_LOG)) + if err != nil { + return err + } + } + return nil +} + +func (l *logsProtoDec) SetOnEntries(h onEntriesHandler) { + l.onEntries = h +} + +var UnmarshalProtoV2 = Build( + withBufferedBody, + withParsedBody(func() proto.Message { return &logproto.PushRequest{} }), + withLogsParser(func(ctx *ParserCtx) iLogsParser { + return &logsProtoDec{ctx: ctx} + })) diff --git a/writer/utils/unmarshal/metricsProtobuf.go b/writer/utils/unmarshal/metricsProtobuf.go new file mode 100644 index 00000000..c161d4eb --- /dev/null +++ b/writer/utils/unmarshal/metricsProtobuf.go @@ -0,0 +1,76 @@ +package unmarshal + +import ( + "github.com/metrico/qryn/writer/model" + "github.com/metrico/qryn/writer/utils/proto/prompb" + "google.golang.org/protobuf/proto" + "time" +) + +type promMetricsProtoDec struct { + ctx *ParserCtx + onEntries onEntriesHandler +} + +func (l *promMetricsProtoDec) Decode() error { + points := 0 + sanitizeLabelsNs := int64(0) + labelsLen := 0 + _labelsLen := 0 + const flushLimit = 1000 + req := l.ctx.bodyObject.(*prompb.WriteRequest) + oLblsBuf := make([][]string, 16) + for _, ts := range req.GetTimeseries() { + oLblsBuf = oLblsBuf[:0] + for _, lbl := range ts.GetLabels() { + oLblsBuf = append(oLblsBuf, []string{lbl.GetName(), lbl.GetValue()}) + labelsLen += len(lbl.GetName()) + len(lbl.GetValue()) + _labelsLen += len(lbl.GetName()) + len(lbl.GetValue()) + } + startSanitizeLabels := time.Now().UnixNano() + oLblsBuf = sanitizeLabels(oLblsBuf) + sanitizeLabelsNs += time.Now().UnixNano() - startSanitizeLabels + + tsns := make([]int64, 0, len(ts.GetSamples())) + value := make([]float64, 0, len(ts.GetSamples())) + msg := make([]string, 0, len(ts.GetSamples())) + + for _, spl := range ts.GetSamples() { + tsns = append(tsns, spl.Timestamp*1e6) + value = append(value, spl.Value) + msg = append(msg, "") + points++ + if points >= flushLimit { + err := l.onEntries(oLblsBuf, tsns, msg, value, + fastFillArray[uint8](len(ts.GetSamples()), model.SAMPLE_TYPE_METRIC)) + if err != nil { + return err + } + // Reset the count and buffers after flushing + points = 0 + tsns = tsns[:0] + value = value[:0] + msg = msg[:0] + } + } + + // Flush remaining samples if sample count is less than maxSamples + if len(tsns) > 0 { + err := l.onEntries(oLblsBuf, tsns, msg, value, + fastFillArray[uint8](len(tsns), model.SAMPLE_TYPE_METRIC)) + if err != nil { + return err + } + } + } + return nil +} + +func (l *promMetricsProtoDec) SetOnEntries(h onEntriesHandler) { + l.onEntries = h +} + +var UnmarshallMetricsWriteProtoV2 = Build( + withBufferedBody, + withParsedBody(func() proto.Message { return &prompb.WriteRequest{} }), + withLogsParser(func(ctx *ParserCtx) iLogsParser { return &promMetricsProtoDec{ctx: ctx} })) diff --git a/writer/utils/unmarshal/otlpUnmarshal.go b/writer/utils/unmarshal/otlpUnmarshal.go new file mode 100644 index 00000000..f487400c --- /dev/null +++ b/writer/utils/unmarshal/otlpUnmarshal.go @@ -0,0 +1,145 @@ +package unmarshal + +import ( + "fmt" + customErrors "github.com/metrico/qryn/writer/utils/errors" + v11 "go.opentelemetry.io/proto/otlp/common/v1" + trace "go.opentelemetry.io/proto/otlp/trace/v1" + "google.golang.org/protobuf/proto" + "strconv" +) + +type OTLPDecoder struct { + ctx *ParserCtx + onSpan onSpanHandler +} + +func getOtlpAttr(attrs []*v11.KeyValue, key string) *v11.KeyValue { + for _, attr := range attrs { + if attr.Key == key { + return attr + } + } + return nil +} + +func otlpGetServiceNames(attrs []*v11.KeyValue) (string, string) { + local := "" + remote := "" + for _, attr := range []string{ + "peer.service", "service.name", "faas.name", "k8s.deployment.name", "process.executable.name", + } { + val := getOtlpAttr(attrs, attr) + if val == nil { + continue + } + _val, ok := val.Value.Value.(*v11.AnyValue_StringValue) + if !ok { + continue + } + local = _val.StringValue + } + for _, attr := range []string{"service.name", "faas.name", "k8s.deployment.name", "process.executable.name"} { + val := getOtlpAttr(attrs, attr) + if val == nil { + continue + } + _val, ok := val.Value.Value.(*v11.AnyValue_StringValue) + if !ok { + continue + } + remote = _val.StringValue + } + if local == "" { + local = "OTLPResourceNoServiceName" + } + return local, remote +} + +func populateServiceNames(span *trace.Span) { + local, remote := otlpGetServiceNames(span.Attributes) + attr := getOtlpAttr(span.Attributes, "service.name") + if attr == nil { + span.Attributes = append(span.Attributes, + &v11.KeyValue{Key: "service.name", Value: &v11.AnyValue{Value: &v11.AnyValue_StringValue{StringValue: local}}}, + ) + } + attr = getOtlpAttr(span.Attributes, "remoteService.name") + if attr == nil { + span.Attributes = append(span.Attributes, + &v11.KeyValue{Key: "remoteService.name", Value: &v11.AnyValue{Value: &v11.AnyValue_StringValue{StringValue: remote}}}, + ) + } +} + +func (d *OTLPDecoder) Decode() error { + obj := d.ctx.bodyObject.(*trace.TracesData) + for _, res := range obj.ResourceSpans { + for _, scope := range res.ScopeSpans { + for _, span := range scope.Spans { + span.Attributes = append(span.Attributes, res.Resource.Attributes...) + attrsMap := map[string]string{} + populateServiceNames(span) + d.initAttributesMap(span.Attributes, "", &attrsMap) + payload, err := proto.Marshal(span) + if err != nil { + return customErrors.NewUnmarshalError(err) + } + attrsMap["name"] = span.Name + keys := make([]string, len(attrsMap)) + vals := make([]string, len(attrsMap)) + i := 0 + for k, v := range attrsMap { + keys[i] = k + vals[i] = v + i++ + } + err = d.onSpan(span.TraceId, span.SpanId, int64(span.StartTimeUnixNano), + int64(span.EndTimeUnixNano-span.StartTimeUnixNano), + string(span.ParentSpanId), span.Name, attrsMap["service.name"], payload, + keys, vals) + if err != nil { + return err + } + } + } + } + return nil +} + +func (d *OTLPDecoder) SetOnEntry(h onSpanHandler) { + d.onSpan = h +} + +func (d *OTLPDecoder) writeAttrValue(key string, val any, prefix string, res *map[string]string) { + switch val.(type) { + case *v11.AnyValue_StringValue: + (*res)[prefix+key] = val.(*v11.AnyValue_StringValue).StringValue + case *v11.AnyValue_BoolValue: + (*res)[prefix+key] = fmt.Sprintf("%v", val.(*v11.AnyValue_BoolValue).BoolValue) + case *v11.AnyValue_DoubleValue: + (*res)[prefix+key] = fmt.Sprintf("%f", val.(*v11.AnyValue_DoubleValue).DoubleValue) + case *v11.AnyValue_IntValue: + (*res)[prefix+key] = fmt.Sprintf("%d", val.(*v11.AnyValue_IntValue).IntValue) + case *v11.AnyValue_ArrayValue: + for i, _val := range val.(*v11.AnyValue_ArrayValue).ArrayValue.Values { + d.writeAttrValue(strconv.FormatInt(int64(i), 10), _val, prefix+key+".", res) + } + case *v11.AnyValue_KvlistValue: + d.initAttributesMap(val.(*v11.AnyValue_KvlistValue).KvlistValue.Values, prefix+key+".", res) + } +} + +func (d *OTLPDecoder) initAttributesMap(attrs any, prefix string, res *map[string]string) { + if _attrs, ok := attrs.([]*v11.KeyValue); ok { + for _, kv := range _attrs { + d.writeAttrValue(kv.Key, kv.Value.Value, prefix, res) + } + } +} + +var UnmarshalOTLPV2 = Build( + withPayloadType(2), + withBufferedBody, + withParsedBody(func() proto.Message { return &trace.TracesData{} }), + withSpansParser(func(ctx *ParserCtx) iSpansParser { return &OTLPDecoder{ctx: ctx} })) diff --git a/writer/utils/unmarshal/otlplogs.go b/writer/utils/unmarshal/otlplogs.go new file mode 100644 index 00000000..3249957d --- /dev/null +++ b/writer/utils/unmarshal/otlplogs.go @@ -0,0 +1,132 @@ +package unmarshal + +import ( + "encoding/base64" + "encoding/json" + "github.com/metrico/qryn/writer/model" + otlpCommon "go.opentelemetry.io/proto/otlp/common/v1" + otlpLogs "go.opentelemetry.io/proto/otlp/logs/v1" + "google.golang.org/protobuf/proto" + "regexp" + "strconv" +) + +type otlpLogDec struct { + ctx *ParserCtx + onEntries onEntriesHandler +} + +func (e *otlpLogDec) Decode() error { + logs := e.ctx.bodyObject.(*otlpLogs.LogsData) + + for _, resLog := range logs.ResourceLogs { + resourceAttrs := map[string]string{} + e.initAttributesMap(resLog.Resource.Attributes, "", &resourceAttrs) + for _, scopeLog := range resLog.ScopeLogs { + scopeAttrs := map[string]string{} + e.initAttributesMap(scopeLog.Scope.Attributes, "", &scopeAttrs) + for _, logRecord := range scopeLog.LogRecords { + var labels [][]string + // Merge resource and scope attributes + attrsMap := make(map[string]string) + for k, v := range resourceAttrs { + attrsMap[k] = v + } + for k, v := range scopeAttrs { + attrsMap[k] = v + } + // Extract log record attributes + e.initAttributesMap(logRecord.Attributes, "", &attrsMap) + + // Extract severity_text and add as level label + if severityText := logRecord.SeverityText; severityText != "" { + attrsMap["level"] = severityText + } + + for k, v := range attrsMap { + labels = append(labels, []string{k, v}) + } + // Extract other log record fields + message := logRecord.Body.GetStringValue() + timestamp := logRecord.TimeUnixNano + // Call onEntries with labels and other details + err := e.onEntries( + labels, + []int64{int64(timestamp)}, + []string{message}, + []float64{0}, + []uint8{model.SAMPLE_TYPE_LOG}, + ) + if err != nil { + return err + } + } + } + } + return nil +} + +func (e *otlpLogDec) initAttributesMap(attrs []*otlpCommon.KeyValue, prefix string, res *map[string]string) { + for _, kv := range attrs { + e.writeAttrValue(kv.Key, kv.Value, prefix, res) + } +} + +func (e *otlpLogDec) writeAttrValue(key string, value *otlpCommon.AnyValue, prefix string, res *map[string]string) { + (*res)[prefix+SanitizeKey(key)] = SanitizeValue(value) +} + +func SanitizeKey(key string) string { + // Replace characters that are not a-z, A-Z, 0-9, or _ with _ + re := regexp.MustCompile(`[^a-zA-Z0-9_]`) + sanitized := re.ReplaceAllString(key, "_") + + // Prefix with _ if the first character is not a-z or A-Z + if len(sanitized) == 0 || (sanitized[0] >= '0' && sanitized[0] <= '9') { + sanitized = "_" + sanitized + } + + return sanitized +} + +func SanitizeValue(value *otlpCommon.AnyValue) string { + switch v := value.Value.(type) { + case *otlpCommon.AnyValue_StringValue: + return v.StringValue + case *otlpCommon.AnyValue_BoolValue: + return strconv.FormatBool(v.BoolValue) + case *otlpCommon.AnyValue_IntValue: + return strconv.FormatInt(v.IntValue, 10) + case *otlpCommon.AnyValue_DoubleValue: + return strconv.FormatFloat(v.DoubleValue, 'f', -1, 64) + case *otlpCommon.AnyValue_BytesValue: + return base64.StdEncoding.EncodeToString(v.BytesValue) + case *otlpCommon.AnyValue_ArrayValue: + items := make([]string, len(v.ArrayValue.Values)) + for i, item := range v.ArrayValue.Values { + items[i] = SanitizeValue(item) + } + jsonItems, _ := json.Marshal(items) + return string(jsonItems) + case *otlpCommon.AnyValue_KvlistValue: + kvMap := make(map[string]string) + for _, kv := range v.KvlistValue.Values { + kvMap[SanitizeKey(kv.Key)] = SanitizeValue(kv.Value) + } + jsonMap, _ := json.Marshal(kvMap) + return string(jsonMap) + default: + return "" + } +} + +func (e *otlpLogDec) SetOnEntries(h onEntriesHandler) { + e.onEntries = h +} + +var UnmarshalOTLPLogsV2 = Build( + withBufferedBody, + withParsedBody(func() proto.Message { return &otlpLogs.LogsData{} }), + withLogsParser(func(ctx *ParserCtx) iLogsParser { + return &otlpLogDec{ctx: ctx} + })) diff --git a/writer/utils/unmarshal/shared.go b/writer/utils/unmarshal/shared.go new file mode 100644 index 00000000..8a44fbd9 --- /dev/null +++ b/writer/utils/unmarshal/shared.go @@ -0,0 +1,59 @@ +package unmarshal + +import ( + "github.com/metrico/qryn/writer/model" + "time" +) + +type timeSeriesAndSamples struct { + ts *model.TimeSeriesData + spl *model.TimeSamplesData + size int + c chan *model.ParserResponse + meta string +} + +func (t *timeSeriesAndSamples) reset() { + t.size = 0 + t.ts = &model.TimeSeriesData{ + MDate: make([]time.Time, 0, 100), + MLabels: make([]string, 0, 100), + MFingerprint: make([]uint64, 0, 100), + MType: make([]uint8, 0, 100), + MMeta: t.meta, + } + t.spl = &model.TimeSamplesData{ + MTimestampNS: make([]int64, 0, 1000), + MFingerprint: make([]uint64, 0, 1000), + MMessage: make([]string, 0, 1000), + MValue: make([]float64, 0, 1000), + } +} + +func (t *timeSeriesAndSamples) flush() { + t.c <- &model.ParserResponse{ + TimeSeriesRequest: t.ts, + SamplesRequest: t.spl, + } +} + +func newTimeSeriesAndSamples(c chan *model.ParserResponse, + meta string) *timeSeriesAndSamples { + res := &timeSeriesAndSamples{ + c: c, + meta: meta, + } + res.reset() + return res +} + +func fastFillArray[T any](len int, val T) []T { + res := make([]T, len) + res[0] = val + _len := 1 + for _len < len { + copy(res[_len:], res[:_len]) + _len <<= 1 + } + return res +} diff --git a/writer/utils/unmarshal/unmarshal.go b/writer/utils/unmarshal/unmarshal.go new file mode 100644 index 00000000..ead5e9ed --- /dev/null +++ b/writer/utils/unmarshal/unmarshal.go @@ -0,0 +1,422 @@ +package unmarshal + +import ( + "bytes" + "fmt" + "github.com/go-faster/city" + "github.com/go-faster/jx" + jsoniter "github.com/json-iterator/go" + clc_writer "github.com/metrico/cloki-config/config/writer" + "github.com/metrico/qryn/writer/config" + customErrors "github.com/metrico/qryn/writer/utils/errors" + "github.com/metrico/qryn/writer/utils/heputils" + "github.com/metrico/qryn/writer/utils/heputils/cityhash102" + "github.com/metrico/qryn/writer/utils/logger" + "regexp" + "strconv" + "strings" + "text/scanner" + "time" + "unsafe" + + "github.com/metrico/qryn/writer/model" +) + +var jsonApi = jsoniter.ConfigCompatibleWithStandardLibrary + +type pushRequestDec struct { + ctx *ParserCtx + onEntries onEntriesHandler + + Labels [][]string + + TsNs []int64 + String []string + Value []float64 + Types []uint8 +} + +func (p *pushRequestDec) Decode() error { + p.TsNs = make([]int64, 0, 1000) + p.String = make([]string, 0, 1000) + p.Value = make([]float64, 0, 1000) + p.Labels = make([][]string, 0, 10) + p.Types = make([]uint8, 0, 1000) + + d := jx.Decode(p.ctx.bodyReader, 64*1024) + return jsonParseError(d.Obj(func(d *jx.Decoder, key string) error { + switch key { + case "streams": + return d.Arr(func(d *jx.Decoder) error { + p.TsNs = p.TsNs[:0] + p.String = p.String[:0] + p.Value = p.Value[:0] + p.Labels = p.Labels[:0] + p.Types = p.Types[:0] + + err := p.decodeStream(d) + if err != nil { + return err + } + return p.onEntries(p.Labels, p.TsNs, p.String, p.Value, p.Types) + }) + default: + d.Skip() + } + return nil + })) +} + +func (p *pushRequestDec) SetOnEntries(h onEntriesHandler) { + p.onEntries = h +} + +func (p *pushRequestDec) decodeStream(d *jx.Decoder) error { + err := d.Obj(func(d *jx.Decoder, key string) error { + switch key { + case "stream": + return p.decodeStreamStream(d) + case "labels": + return p.decodeStreamLabels(d) + case "values": + return p.decodeStreamValues(d) + case "entries": + return p.decodeStreamEntries(d) + default: + d.Skip() + } + return nil + }) + return err +} + +func (p *pushRequestDec) decodeStreamStream(d *jx.Decoder) error { + err := d.Obj(func(d *jx.Decoder, key string) error { + val, err := d.Str() + if err != nil { + return customErrors.NewUnmarshalError(err) + } + p.Labels = append(p.Labels, []string{key, val}) + return nil + }) + if err != nil { + return customErrors.NewUnmarshalError(err) + } + + p.Labels = sanitizeLabels(p.Labels) + + return nil +} + +func (p *pushRequestDec) decodeStreamLabels(d *jx.Decoder) error { + labelsBytes, err := d.StrBytes() + if err != nil { + return customErrors.NewUnmarshalError(err) + } + p.Labels, err = parseLabelsLokiFormat(labelsBytes, p.Labels) + if err != nil { + return customErrors.NewUnmarshalError(err) + } + p.Labels = sanitizeLabels(p.Labels) + return err +} + +func (p *pushRequestDec) decodeStreamValues(d *jx.Decoder) error { + return d.Arr(func(d *jx.Decoder) error { + return p.decodeStreamValue(d) + }) +} + +func (p *pushRequestDec) decodeStreamValue(d *jx.Decoder) error { + j := -1 + var ( + tsNs int64 + str string + val float64 + err error + tp uint8 + ) + err = d.Arr(func(d *jx.Decoder) error { + j++ + switch j { + case 0: + strTsNs, err := d.Str() + if err != nil { + return customErrors.NewUnmarshalError(err) + } + tsNs, err = strconv.ParseInt(strTsNs, 10, 64) + return err + case 1: + str, err = d.Str() + tp |= model.SAMPLE_TYPE_LOG + return err + case 2: + val, err = d.Float64() + tp |= model.SAMPLE_TYPE_METRIC + return err + default: + d.Skip() + } + return nil + }) + + if tp == 3 { + tp = 0 + } + + if err != nil { + return customErrors.NewUnmarshalError(err) + } + + p.TsNs = append(p.TsNs, tsNs) + p.String = append(p.String, str) + p.Value = append(p.Value, val) + p.Types = append(p.Types, tp) + + return nil +} + +func (p *pushRequestDec) decodeStreamEntries(d *jx.Decoder) error { + return d.Arr(func(d *jx.Decoder) error { + return p.decodeStreamEntry(d) + }) +} + +func (p *pushRequestDec) decodeStreamEntry(d *jx.Decoder) error { + var ( + tsNs int64 + str string + val float64 + err error + tp uint8 + ) + err = d.Obj(func(d *jx.Decoder, key string) error { + switch key { + case "ts": + bTs, err := d.StrBytes() + if err != nil { + return err + } + tsNs, err = parseTime(bTs) + return err + case "timestamp": + bTs, err := d.StrBytes() + if err != nil { + return err + } + tsNs, err = parseTime(bTs) + return err + case "line": + str, err = d.Str() + tp |= model.SAMPLE_TYPE_LOG + return err + case "value": + val, err = d.Float64() + tp |= model.SAMPLE_TYPE_METRIC + return err + default: + return d.Skip() + } + return nil + }) + if err != nil { + return customErrors.NewUnmarshalError(err) + } + + if tp == 3 { + tp = 0 + } + + p.TsNs = append(p.TsNs, tsNs) + p.String = append(p.String, str) + p.Value = append(p.Value, val) + p.Types = append(p.Types, tp) + if err != nil { + return customErrors.NewUnmarshalError(err) + } + return nil +} + +var DecodePushRequestStringV2 = Build( + withLogsParser(func(ctx *ParserCtx) iLogsParser { return &pushRequestDec{ctx: ctx} })) + +func encodeLabels(lbls [][]string) string { + arrLbls := make([]string, len(lbls)) + for i, l := range lbls { + arrLbls[i] = fmt.Sprintf("%s:%s", strconv.Quote(l[0]), strconv.Quote(l[1])) + } + return fmt.Sprintf("{%s}", strings.Join(arrLbls, ",")) +} + +func fingerprintLabels(lbls [][]string) uint64 { + determs := []uint64{0, 0, 1} + for _, lbl := range lbls { + hash := cityhash102.Hash128to64(cityhash102.Uint128{ + city.CH64([]byte(lbl[0])), + city.CH64([]byte(lbl[1])), + }) + determs[0] = determs[0] + hash + determs[1] = determs[1] ^ hash + determs[2] = determs[2] * (1779033703 + 2*hash) + } + fingerByte := unsafe.Slice((*byte)(unsafe.Pointer(&determs[0])), 24) + var fingerPrint uint64 + switch config.Cloki.Setting.FingerPrintType { + case clc_writer.FINGERPRINT_CityHash: + fingerPrint = city.CH64(fingerByte) + case clc_writer.FINGERPRINT_Bernstein: + fingerPrint = uint64(heputils.FingerprintLabelsDJBHashPrometheus(fingerByte)) + } + return fingerPrint +} + +var sanitizeRe = regexp.MustCompile("(^[^a-zA-Z_]|[^a-zA-Z0-9_])") + +func sanitizeLabels(lbls [][]string) [][]string { + for i, _ := range lbls { + lbls[i][0] = sanitizeRe.ReplaceAllString(lbls[i][0], "_") + if len(lbls[i][1]) > 100 { + lbls[i][1] = lbls[i][1][:100] + "..." + } + } + return lbls +} + +func getFingerIndexbyName(lbls [][]string, label string) (int, error) { + + for index, val := range lbls { + if val[0] == label { + return index, nil + } + } + return 0, customErrors.ErrNotFound +} + +func parseTime(b []byte) (int64, error) { + //2021-12-26T16:00:06.944Z + var err error + if b != nil { + var timestamp int64 + val := string(b) + if strings.ContainsAny(val, ":-TZ") { + t, e := time.Parse(time.RFC3339, val) + if e != nil { + + logger.Debug("ERROR unmarshaling this string: ", e.Error()) + return 0, customErrors.NewUnmarshalError(e) + } + return t.UTC().UnixNano(), nil + } else { + timestamp, err = strconv.ParseInt(val, 10, 64) + if err != nil { + logger.Debug("ERROR unmarshaling this NS: ", val, err) + return 0, customErrors.NewUnmarshalError(err) + } + } + return timestamp, nil + } else { + err = fmt.Errorf("bad byte array for Unmarshaling") + logger.Debug("bad data: ", err) + return 0, customErrors.NewUnmarshalError(err) + } +} + +func parseLabelsLokiFormat(labels []byte, buf [][]string) ([][]string, error) { + s := scanner.Scanner{} + s.Init(bytes.NewReader(labels)) + errorF := func() ([][]string, error) { + return nil, fmt.Errorf("unknown input: %s", labels[s.Offset:]) + } + tok := s.Scan() + checkRune := func(expect rune, strExpect string) bool { + return tok == expect && (strExpect == "" || s.TokenText() == strExpect) + } + if !checkRune(123, "{") { + return errorF() + } + for tok != scanner.EOF { + tok = s.Scan() + if !checkRune(scanner.Ident, "") { + return errorF() + } + name := s.TokenText() + tok = s.Scan() + if !checkRune(61, "=") { + return errorF() + } + tok = s.Scan() + if !checkRune(scanner.String, "") { + return errorF() + } + val, err := strconv.Unquote(s.TokenText()) + if err != nil { + return nil, customErrors.NewUnmarshalError(err) + } + tok = s.Scan() + buf = append(buf, []string{name, val}) + if checkRune(125, "}") { + return buf, nil + } + if !checkRune(44, ",") { + return errorF() + } + } + return buf, nil +} + +/* +// NewPushRequest constructs a logproto.PushRequest from a PushRequest +func NewPushRequest(r loghttp.PushRequest) logproto.PushRequest { + ret := logproto.PushRequest{ + Streams: make([]logproto.Stream, len(r.Streams)), + } + + for i, s := range r.Streams { + ret.Streams[i] = NewStream(s) + } + + return ret +} + +// NewPushRequest constructs a logproto.PushRequest from a PushRequest +func NewPushRequestLog(r model.PushRequest) logproto.PushRequest { + ret := logproto.PushRequest{ + Streams: make([]logproto.Stream, len(r.Streams)), + } + for i, s := range r.Streams { + ret.Streams[i] = NewStreamLog(&s) + } + + return ret +} + +// NewStream constructs a logproto.Stream from a Stream +func NewStream(s *loghttp.Stream) logproto.Stream { + return logproto.Stream{ + Entries: *(*[]logproto.Entry)(unsafe.Pointer(&s.Entries)), + Labels: s.Labels.String(), + } +} + +// NewStream constructs a logproto.Stream from a Stream +func NewStreamLog(s *model.Stream) logproto.Stream { + return logproto.Stream{ + Entries: *(*[]logproto.Entry)(unsafe.Pointer(&s.Entries)), + Labels: s.Labels, + } +} + +// WebsocketReader knows how to read message to a websocket connection. +type WebsocketReader interface { + ReadMessage() (int, []byte, error) +} + +// ReadTailResponseJSON unmarshals the loghttp.TailResponse from a websocket reader. +func ReadTailResponseJSON(r *loghttp.TailResponse, reader WebsocketReader) error { + _, data, err := reader.ReadMessage() + if err != nil { + return err + } + return jsoniter.Unmarshal(data, r) +} +*/ diff --git a/writer/utils/unmarshal/zipkinJsonUnmarshal.go b/writer/utils/unmarshal/zipkinJsonUnmarshal.go new file mode 100644 index 00000000..1d3b67bf --- /dev/null +++ b/writer/utils/unmarshal/zipkinJsonUnmarshal.go @@ -0,0 +1,241 @@ +package unmarshal + +import ( + "bufio" + "encoding/hex" + "fmt" + "github.com/go-faster/jx" + custom_errors "github.com/metrico/qryn/writer/utils/errors" + + "strconv" +) + +func jsonParseError(err error) error { + if err == nil { + return nil + } + return custom_errors.NewUnmarshalError(err) + //return fmt.Errorf("json parse error: %v", err) +} + +type zipkinDecoderV2 struct { + ctx *ParserCtx + onSpan onSpanHandler + + traceId []byte + spanId []byte + timestampNs int64 + durationNs int64 + parentId string + name string + serviceName string + payload []byte + key []string + val []string +} + +func (z *zipkinDecoderV2) SetOnEntry(h onSpanHandler) { + z.onSpan = h +} + +func (z *zipkinDecoderV2) Decode() error { + z.key = make([]string, 10) + z.val = make([]string, 10) + dec := jx.Decode(z.ctx.bodyReader, 64*1024) + return dec.Arr(func(d *jx.Decoder) error { + z.traceId = nil + z.spanId = nil + z.timestampNs = 0 + z.durationNs = 0 + z.parentId = "" + z.name = "" + z.serviceName = "" + z.payload = nil + z.key = z.key[:0] + z.val = z.val[:0] + rawSpan, err := dec.Raw() + if err != nil { + return custom_errors.NewUnmarshalError(err) + } + z.payload = append([]byte{}, rawSpan...) + return z.decodeSpan(rawSpan) + }) + +} + +func (z *zipkinDecoderV2) decodeSpan(rawSpan jx.Raw) error { + dec := jx.DecodeBytes(rawSpan) + if rawSpan.Type() != jx.Object { + return custom_errors.New400Error(fmt.Sprintf("span %s is not an object", rawSpan.String())) + } + + err := dec.Obj(func(d *jx.Decoder, key string) error { + switch key { + case "traceId": + hexTid, err := dec.StrBytes() + if err != nil { + return err + } + z.traceId, err = z.decodeHexStr(hexTid, 32) + return err + case "id": + hexSpanId, err := dec.StrBytes() + if err != nil { + return err + } + z.spanId, err = z.decodeHexStr(hexSpanId, 16) + return err + case "parentId": + parentId, err := d.StrBytes() + if err != nil { + return err + } + rawParentId, err := z.decodeHexStr(parentId, 16) + z.parentId = string(rawParentId) + return err + case "timestamp": + var err error + z.timestampNs, err = z.stringOrInt64(d) + z.timestampNs *= 1000 + return err + case "duration": + val, err := z.stringOrInt64(d) + z.durationNs = val * 1000 + return err + case "name": + var err error + z.name, err = d.Str() + z.key = append(z.key, "name") + z.val = append(z.val, z.name) + return err + case "localEndpoint": + serviceName, err := z.parseEndpoint(d, "local_endpoint_") + if err != nil { + return err + } + z.serviceName = serviceName + return nil + case "remoteEndpoint": + serviceName, err := z.parseEndpoint(d, "remote_endpoint_") + if err != nil { + return err + } + if z.serviceName != "" { + z.serviceName = serviceName + } + return nil + case "tags": + err := z.parseTags(d) + return err + default: + d.Skip() + } + return nil + }) + if err != nil { + return custom_errors.NewUnmarshalError(err) + } + z.key = append(z.key, "service.name") + z.val = append(z.val, z.serviceName) + return z.onSpan(z.traceId, z.spanId, z.timestampNs, z.durationNs, z.parentId, + z.name, z.serviceName, z.payload, z.key, z.val) +} + +func (z *zipkinDecoderV2) stringOrInt64(d *jx.Decoder) (int64, error) { + next := d.Next() + switch next { + case jx.Number: + return d.Int64() + case jx.String: + str, err := d.Str() + if err != nil { + return 0, custom_errors.NewUnmarshalError(err) + } + return strconv.ParseInt(str, 10, 64) + } + return 0, custom_errors.NewUnmarshalError(fmt.Errorf("format not supported")) +} + +func (z *zipkinDecoderV2) parseEndpoint(d *jx.Decoder, prefix string) (string, error) { + serviceName := "" + err := d.Obj(func(d *jx.Decoder, key string) error { + switch key { + case "serviceName": + val, err := d.Str() + if err != nil { + return custom_errors.NewUnmarshalError(err) + } + z.key = append(z.key, prefix+"service_name") + z.val = append(z.val, val) + serviceName = val + default: + return d.Skip() + } + return nil + }) + return serviceName, err +} + +func (z *zipkinDecoderV2) parseTags(d *jx.Decoder) error { + return d.Obj(func(d *jx.Decoder, key string) error { + tp := d.Next() + if tp != jx.String { + return d.Skip() + } + z.key = append(z.key, key) + val, err := d.Str() + if err != nil { + return custom_errors.NewUnmarshalError(err) + } + z.val = append(z.val, val) + return nil + }) +} + +func (z *zipkinDecoderV2) decodeHexStr(hexStr []byte, leng int) ([]byte, error) { + if len(hexStr) == 0 { + return nil, custom_errors.New400Error("hex string is zero") + + } + if len(hexStr) < leng { + prefix := make([]byte, leng) + for i := 0; i < leng; i++ { + prefix[i] = '0' + } + copy(prefix[leng-len(hexStr):], hexStr) + hexStr = prefix + } + hexStr = hexStr[:leng] + res := make([]byte, leng/2) + _, err := hex.Decode(res, hexStr) + if err != nil { + return nil, custom_errors.NewUnmarshalError(err) + } + return res, err +} + +type zipkinNDDecoderV2 struct { + *zipkinDecoderV2 +} + +func (z *zipkinNDDecoderV2) Decode() error { + scanner := bufio.NewScanner(z.ctx.bodyReader) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + err := z.decodeSpan(scanner.Bytes()) + if err != nil { + return custom_errors.NewUnmarshalError(err) + } + } + return nil +} + +var UnmarshalZipkinNDJSONV2 = Build( + withPayloadType(1), + withSpansParser(func(ctx *ParserCtx) iSpansParser { + return &zipkinNDDecoderV2{&zipkinDecoderV2{ctx: ctx}} + })) + +var UnmarshalZipkinJSONV2 = Build( + withPayloadType(1), + withSpansParser(func(ctx *ParserCtx) iSpansParser { return &zipkinDecoderV2{ctx: ctx} })) diff --git a/writer/utils/util/conv.go b/writer/utils/util/conv.go new file mode 100644 index 00000000..6022db66 --- /dev/null +++ b/writer/utils/util/conv.go @@ -0,0 +1,32 @@ +package util + +import ( + "math" + "time" + "unsafe" + + "github.com/prometheus/common/model" +) + +// ModelLabelSetToMap convert a model.LabelSet to a map[string]string +func ModelLabelSetToMap(m model.LabelSet) map[string]string { + if len(m) == 0 { + return map[string]string{} + } + return *(*map[string]string)(unsafe.Pointer(&m)) +} + +// MapToModelLabelSet converts a map into a model.LabelSet +func MapToModelLabelSet(m map[string]string) model.LabelSet { + if len(m) == 0 { + return model.LabelSet{} + } + return *(*map[model.LabelName]model.LabelValue)(unsafe.Pointer(&m)) +} + +// RoundToMilliseconds returns milliseconds precision time from nanoseconds. +// from will be rounded down to the nearest milliseconds while through is rounded up. +func RoundToMilliseconds(from, through time.Time) (model.Time, model.Time) { + return model.Time(int64(math.Floor(float64(from.UnixNano()) / float64(time.Millisecond)))), + model.Time(int64(math.Ceil(float64(through.UnixNano()) / float64(time.Millisecond)))) +} diff --git a/writer/utils/util/conv_test.go b/writer/utils/util/conv_test.go new file mode 100644 index 00000000..de6d08ce --- /dev/null +++ b/writer/utils/util/conv_test.go @@ -0,0 +1,130 @@ +package util + +import ( + "reflect" + "testing" + "time" + + "github.com/prometheus/common/model" +) + +func TestRoundToMilliseconds(t *testing.T) { + tests := []struct { + name string + from time.Time + through time.Time + wantFrom model.Time + wantThrough model.Time + }{ + { + "0", + time.Unix(0, 0), + time.Unix(0, 1), + model.Time(0), + model.Time(1), + }, + { + "equal", + time.Unix(0, time.Millisecond.Nanoseconds()), + time.Unix(0, time.Millisecond.Nanoseconds()), + model.Time(1), + model.Time(1), + }, + { + "exact", + time.Unix(0, time.Millisecond.Nanoseconds()), + time.Unix(0, 2*time.Millisecond.Nanoseconds()), + model.Time(1), + model.Time(2), + }, + { + "rounding", + time.Unix(0, time.Millisecond.Nanoseconds()+10), + time.Unix(0, 2*time.Millisecond.Nanoseconds()+10), + model.Time(1), + model.Time(3), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + from, through := RoundToMilliseconds(tt.from, tt.through) + if !reflect.DeepEqual(from, tt.wantFrom) { + t.Errorf("RoundToMilliseconds() from = %v, want %v", from, tt.wantFrom) + } + if !reflect.DeepEqual(through, tt.wantThrough) { + t.Errorf("RoundToMilliseconds() through = %v, want %v", through, tt.wantThrough) + } + }) + } +} + +func TestModelLabelSetToMap(t *testing.T) { + + tests := []struct { + name string + m model.LabelSet + want map[string]string + }{ + { + "nil", + nil, + map[string]string{}, + }, + { + "one", + model.LabelSet{model.LabelName("foo"): model.LabelValue("bar")}, + map[string]string{"foo": "bar"}, + }, + { + "two", + model.LabelSet{ + model.LabelName("foo"): model.LabelValue("bar"), + model.LabelName("buzz"): model.LabelValue("fuzz"), + }, + map[string]string{ + "foo": "bar", + "buzz": "fuzz", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ModelLabelSetToMap(tt.m); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ModelLabelSetToMap() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMapToModelLabelSet(t *testing.T) { + tests := []struct { + name string + args map[string]string + want model.LabelSet + }{ + {"nil", nil, model.LabelSet{}}, + { + "one", + map[string]string{"foo": "bar"}, + model.LabelSet{model.LabelName("foo"): model.LabelValue("bar")}, + }, + { + "two", + map[string]string{ + "foo": "bar", + "buzz": "fuzz", + }, + model.LabelSet{ + model.LabelName("foo"): model.LabelValue("bar"), + model.LabelName("buzz"): model.LabelValue("fuzz"), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := MapToModelLabelSet(tt.args); !reflect.DeepEqual(got, tt.want) { + t.Errorf("MapToModelLabelSet() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/writer/utils/util/flagext/bytesize.go b/writer/utils/util/flagext/bytesize.go new file mode 100644 index 00000000..68531497 --- /dev/null +++ b/writer/utils/util/flagext/bytesize.go @@ -0,0 +1,46 @@ +package flagext + +import ( + "strings" + + "github.com/c2h5oh/datasize" +) + +// ByteSize is a flag parsing compatibility type for constructing human friendly sizes. +// It implements flag.Value & flag.Getter. +type ByteSize uint64 + +func (bs ByteSize) String() string { + return datasize.ByteSize(bs).String() +} + +func (bs *ByteSize) Set(s string) error { + var v datasize.ByteSize + + // Bytesize currently doesn't handle things like Mb, but only handles MB. + // Therefore we capitalize just for convenience + if err := v.UnmarshalText([]byte(strings.ToUpper(s))); err != nil { + return err + } + *bs = ByteSize(v.Bytes()) + return nil +} + +func (bs ByteSize) Get() interface{} { + return bs.Val() +} + +func (bs ByteSize) Val() int { + return int(bs) +} + +// / UnmarshalYAML the Unmarshaler interface of the yaml pkg. +func (bs *ByteSize) UnmarshalYAML(unmarshal func(interface{}) error) error { + var str string + err := unmarshal(&str) + if err != nil { + return err + } + + return bs.Set(str) +} diff --git a/writer/utils/util/flagext/bytesize_test.go b/writer/utils/util/flagext/bytesize_test.go new file mode 100644 index 00000000..95331af9 --- /dev/null +++ b/writer/utils/util/flagext/bytesize_test.go @@ -0,0 +1,104 @@ +package flagext + +import ( + "testing" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" +) + +func Test_ByteSize(t *testing.T) { + for _, tc := range []struct { + in string + err bool + out int + }{ + { + in: "abc", + err: true, + }, + { + in: "", + err: false, + out: 0, + }, + { + in: "0", + err: false, + out: 0, + }, + { + in: "1b", + err: false, + out: 1, + }, + { + in: "100kb", + err: false, + out: 100 << 10, + }, + { + in: "100 KB", + err: false, + out: 100 << 10, + }, + { + // ensure lowercase works + in: "50mb", + err: false, + out: 50 << 20, + }, + { + // ensure mixed capitalization works + in: "50Mb", + err: false, + out: 50 << 20, + }, + { + in: "256GB", + err: false, + out: 256 << 30, + }, + } { + t.Run(tc.in, func(t *testing.T) { + var bs ByteSize + + err := bs.Set(tc.in) + if tc.err { + require.NotNil(t, err) + } else { + require.Nil(t, err) + require.Equal(t, tc.out, bs.Get().(int)) + } + + }) + } +} + +func Test_ByteSizeYAML(t *testing.T) { + for _, tc := range []struct { + in string + err bool + out ByteSize + }{ + { + in: "256GB", + out: ByteSize(256 << 30), + }, + { + in: "abc", + err: true, + }, + } { + t.Run(tc.in, func(t *testing.T) { + var out ByteSize + err := yaml.Unmarshal([]byte(tc.in), &out) + if tc.err { + require.NotNil(t, err) + } else { + require.Nil(t, err) + require.Equal(t, tc.out, out) + } + }) + } +} diff --git a/writer/utils/util/list.go b/writer/utils/util/list.go new file mode 100644 index 00000000..0fba8994 --- /dev/null +++ b/writer/utils/util/list.go @@ -0,0 +1,40 @@ +package util + +func MergeStringLists(ss ...[]string) []string { + switch len(ss) { + case 0: + return nil + case 1: + return ss[0] + case 2: + return MergeStringPair(ss[0], ss[1]) + default: + n := len(ss) / 2 + return MergeStringPair(MergeStringLists(ss[:n]...), MergeStringLists(ss[n:]...)) + } +} + +func MergeStringPair(s1, s2 []string) []string { + i, j := 0, 0 + result := make([]string, 0, len(s1)+len(s2)) + for i < len(s1) && j < len(s2) { + if s1[i] < s2[j] { + result = append(result, s1[i]) + i++ + } else if s1[i] > s2[j] { + result = append(result, s2[j]) + j++ + } else { + result = append(result, s1[i]) + i++ + j++ + } + } + for ; i < len(s1); i++ { + result = append(result, s1[i]) + } + for ; j < len(s2); j++ { + result = append(result, s2[j]) + } + return result +} diff --git a/writer/utils/util/log.go b/writer/utils/util/log.go new file mode 100644 index 00000000..8d2753b9 --- /dev/null +++ b/writer/utils/util/log.go @@ -0,0 +1,74 @@ +package util + +import ( + "fmt" + "os" + "strings" + + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" +) + +type LogAdapter struct { + log.Logger +} + +func NewLogAdapter(l log.Logger) LogAdapter { + return LogAdapter{ + Logger: l, + } +} + +// Fatal implements tail.logger +func (l LogAdapter) Fatal(v ...interface{}) { + level.Error(l).Log("msg", fmt.Sprint(v...)) + os.Exit(1) +} + +// Fatalf implements tail.logger +func (l LogAdapter) Fatalf(format string, v ...interface{}) { + level.Error(l).Log("msg", fmt.Sprintf(strings.TrimSuffix(format, "\n"), v...)) + os.Exit(1) +} + +// Fatalln implements tail.logger +func (l LogAdapter) Fatalln(v ...interface{}) { + level.Error(l).Log("msg", fmt.Sprint(v...)) + os.Exit(1) +} + +// Panic implements tail.logger +func (l LogAdapter) Panic(v ...interface{}) { + s := fmt.Sprint(v...) + level.Error(l).Log("msg", s) + panic(s) +} + +// Panicf implements tail.logger +func (l LogAdapter) Panicf(format string, v ...interface{}) { + s := fmt.Sprintf(strings.TrimSuffix(format, "\n"), v...) + level.Error(l).Log("msg", s) + panic(s) +} + +// Panicln implements tail.logger +func (l LogAdapter) Panicln(v ...interface{}) { + s := fmt.Sprint(v...) + level.Error(l).Log("msg", s) + panic(s) +} + +// Print implements tail.logger +func (l LogAdapter) Print(v ...interface{}) { + level.Info(l).Log("msg", fmt.Sprint(v...)) +} + +// Printf implements tail.logger +func (l LogAdapter) Printf(format string, v ...interface{}) { + level.Info(l).Log("msg", fmt.Sprintf(strings.TrimSuffix(format, "\n"), v...)) +} + +// Println implements tail.logger +func (l LogAdapter) Println(v ...interface{}) { + level.Info(l).Log("msg", fmt.Sprint(v...)) +} diff --git a/writer/utils/util/mock.go b/writer/utils/util/mock.go new file mode 100644 index 00000000..fdb4330c --- /dev/null +++ b/writer/utils/util/mock.go @@ -0,0 +1,21 @@ +package util + +import ( + "github.com/stretchr/testify/mock" +) + +type ExtendedMock struct { + mock.Mock +} + +func (m *ExtendedMock) GetMockedCallsByMethod(method string) []mock.Call { + calls := make([]mock.Call, 0) + + for _, call := range m.Calls { + if call.Method == method { + calls = append(calls, call) + } + } + + return calls +} diff --git a/writer/utils/util/pool/bytesbuffer.go b/writer/utils/util/pool/bytesbuffer.go new file mode 100644 index 00000000..0a20a4fc --- /dev/null +++ b/writer/utils/util/pool/bytesbuffer.go @@ -0,0 +1,69 @@ +package pool + +import ( + "bytes" + "sync" +) + +// BufferPool is a bucketed pool for variably bytes buffers. +type BufferPool struct { + buckets []sync.Pool + sizes []int +} + +// NewBuffer a new Pool with size buckets for minSize to maxSize +// increasing by the given factor. +func NewBuffer(minSize, maxSize int, factor float64) *BufferPool { + if minSize < 1 { + panic("invalid minimum pool size") + } + if maxSize < 1 { + panic("invalid maximum pool size") + } + if factor < 1 { + panic("invalid factor") + } + + var sizes []int + + for s := minSize; s <= maxSize; s = int(float64(s) * factor) { + sizes = append(sizes, s) + } + + return &BufferPool{ + buckets: make([]sync.Pool, len(sizes)), + sizes: sizes, + } +} + +// Get returns a byte buffer that fits the given size. +func (p *BufferPool) Get(sz int) *bytes.Buffer { + for i, bktSize := range p.sizes { + if sz > bktSize { + continue + } + b := p.buckets[i].Get() + if b == nil { + b = bytes.NewBuffer(make([]byte, 0, bktSize)) + } + buf := b.(*bytes.Buffer) + buf.Reset() + return b.(*bytes.Buffer) + } + return bytes.NewBuffer(make([]byte, 0, sz)) +} + +// Put adds a byte buffer to the right bucket in the pool. +func (p *BufferPool) Put(s *bytes.Buffer) { + if s == nil { + return + } + cap := s.Cap() + for i, size := range p.sizes { + if cap > size { + continue + } + p.buckets[i].Put(s) + return + } +} diff --git a/writer/utils/util/pool/bytesbuffer_test.go b/writer/utils/util/pool/bytesbuffer_test.go new file mode 100644 index 00000000..0fa955d0 --- /dev/null +++ b/writer/utils/util/pool/bytesbuffer_test.go @@ -0,0 +1,17 @@ +package pool + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_ZeroBuffer(t *testing.T) { + p := NewBuffer(2, 10, 2) + require.Equal(t, 0, p.Get(1).Len()) + require.Equal(t, 0, p.Get(1).Len()) + require.Equal(t, 0, p.Get(2).Len()) + require.Equal(t, 0, p.Get(2).Len()) + require.Equal(t, 0, p.Get(20).Len()) + require.Equal(t, 0, p.Get(20).Len()) +} diff --git a/writer/utils/util/query_string_builder.go b/writer/utils/util/query_string_builder.go new file mode 100644 index 00000000..f939c3b9 --- /dev/null +++ b/writer/utils/util/query_string_builder.go @@ -0,0 +1,48 @@ +package util + +import ( + "net/url" + "strconv" +) + +type QueryStringBuilder struct { + values url.Values +} + +func NewQueryStringBuilder() *QueryStringBuilder { + return &QueryStringBuilder{ + values: url.Values{}, + } +} + +func (b *QueryStringBuilder) SetString(name, value string) { + b.values.Set(name, value) +} + +func (b *QueryStringBuilder) SetStringArray(name string, values []string) { + for _, v := range values { + b.values.Add(name, v) + } +} + +func (b *QueryStringBuilder) SetInt(name string, value int64) { + b.SetString(name, strconv.FormatInt(value, 10)) +} + +func (b *QueryStringBuilder) SetInt32(name string, value int) { + b.SetString(name, strconv.Itoa(value)) +} + +func (b *QueryStringBuilder) SetFloat(name string, value float64) { + b.SetString(name, strconv.FormatFloat(value, 'f', -1, 64)) +} + +func (b *QueryStringBuilder) SetFloat32(name string, value float32) { + b.SetString(name, strconv.FormatFloat(float64(value), 'f', -1, 32)) +} + +// Encode returns the URL-encoded query string based on key-value +// parameters added to the builder calling Set functions. +func (b *QueryStringBuilder) Encode() string { + return b.values.Encode() +} diff --git a/writer/utils/util/query_string_builder_test.go b/writer/utils/util/query_string_builder_test.go new file mode 100644 index 00000000..a97d5971 --- /dev/null +++ b/writer/utils/util/query_string_builder_test.go @@ -0,0 +1,61 @@ +package util + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestQueryStringBuilder(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + input map[string]interface{} + expectedEncoded string + }{ + "should return an empty query string on no params": { + input: map[string]interface{}{}, + expectedEncoded: "", + }, + "should return the URL encoded query string parameters": { + input: map[string]interface{}{ + "float32": float32(123.456), + "float64": float64(123.456), + "float64int": float64(12345.0), + "int32": 32, + "int64": int64(64), + "string": "foo", + }, + expectedEncoded: "float32=123.456&float64=123.456&float64int=12345&int32=32&int64=64&string=foo", + }, + } + + for testName, testData := range tests { + testData := testData + + t.Run(testName, func(t *testing.T) { + params := NewQueryStringBuilder() + + for name, value := range testData.input { + switch value := value.(type) { + case string: + params.SetString(name, value) + case float32: + params.SetFloat32(name, value) + case float64: + params.SetFloat(name, value) + case int: + params.SetInt32(name, value) + case int64: + params.SetInt(name, value) + default: + require.Fail(t, fmt.Sprintf("Unknown data type for test fixture with name '%s'", name)) + } + } + + assert.Equal(t, testData.expectedEncoded, params.Encode()) + }) + } +} diff --git a/writer/utils/util/reader.go b/writer/utils/util/reader.go new file mode 100644 index 00000000..47a57641 --- /dev/null +++ b/writer/utils/util/reader.go @@ -0,0 +1,31 @@ +package util + +import ( + "io" +) + +type sizeReader struct { + size int64 + r io.Reader +} + +type SizeReader interface { + io.Reader + Size() int64 +} + +// NewSizeReader returns an io.Reader that will have the number of bytes +// read from r available. +func NewSizeReader(r io.Reader) SizeReader { + return &sizeReader{r: r} +} + +func (v *sizeReader) Read(p []byte) (int, error) { + n, err := v.r.Read(p) + v.size += int64(n) + return n, err +} + +func (v *sizeReader) Size() int64 { + return v.size +} diff --git a/writer/utils/util/ring.go b/writer/utils/util/ring.go new file mode 100644 index 00000000..96f97e34 --- /dev/null +++ b/writer/utils/util/ring.go @@ -0,0 +1,11 @@ +package util + +import "hash/fnv" + +// TokenFor generates a token used for finding ingesters from ring +func TokenFor(userID, labels string) uint32 { + h := fnv.New32() + _, _ = h.Write([]byte(userID)) + _, _ = h.Write([]byte(labels)) + return h.Sum32() +} diff --git a/writer/utils/util/runtime/config.go b/writer/utils/util/runtime/config.go new file mode 100644 index 00000000..110a9fce --- /dev/null +++ b/writer/utils/util/runtime/config.go @@ -0,0 +1,56 @@ +package runtime + +type Config struct { + LogStreamCreation bool `yaml:"log_stream_creation"` + LogPushRequest bool `yaml:"log_push_request"` + LogPushRequestStreams bool `yaml:"log_push_request_streams"` +} + +// TenantConfig is a function that returns configs for given tenant, or +// nil, if there are no tenant-specific configs. +type TenantConfig func(userID string) *Config + +// TenantConfigs periodically fetch a set of per-user configs, and provides convenience +// functions for fetching the correct value. +type TenantConfigs struct { + defaultConfig *Config + tenantConfig TenantConfig +} + +// DefaultTenantConfigs creates and returns a new TenantConfigs with the defaults populated. +func DefaultTenantConfigs() *TenantConfigs { + return &TenantConfigs{ + defaultConfig: &Config{}, + tenantConfig: nil, + } +} + +// NewTenantConfig makes a new TenantConfigs +func NewTenantConfigs(tenantConfig TenantConfig) (*TenantConfigs, error) { + return &TenantConfigs{ + defaultConfig: DefaultTenantConfigs().defaultConfig, + tenantConfig: tenantConfig, + }, nil +} + +func (o *TenantConfigs) getOverridesForUser(userID string) *Config { + if o.tenantConfig != nil { + l := o.tenantConfig(userID) + if l != nil { + return l + } + } + return o.defaultConfig +} + +func (o *TenantConfigs) LogStreamCreation(userID string) bool { + return o.getOverridesForUser(userID).LogStreamCreation +} + +func (o *TenantConfigs) LogPushRequest(userID string) bool { + return o.getOverridesForUser(userID).LogPushRequest +} + +func (o *TenantConfigs) LogPushRequestStreams(userID string) bool { + return o.getOverridesForUser(userID).LogPushRequestStreams +} diff --git a/writer/utils/util/string.go b/writer/utils/util/string.go new file mode 100644 index 00000000..3312fb89 --- /dev/null +++ b/writer/utils/util/string.go @@ -0,0 +1,34 @@ +package util + +import ( + "bytes" + "fmt" + "unicode" +) + +func StringRef(value string) *string { + return &value +} + +func StringSliceContains(slice []string, value string) bool { + for _, item := range slice { + if item == value { + return true + } + } + + return false +} + +// SnakeCase converts given string `s` into `snake_case`. +func SnakeCase(s string) string { + var buf bytes.Buffer + for i, r := range s { + if unicode.IsUpper(r) && i > 0 && s[i-1] != '_' { + fmt.Fprintf(&buf, "_") + } + r = unicode.ToLower(r) + fmt.Fprintf(&buf, "%c", r) + } + return buf.String() +} diff --git a/writer/utils/util/string_test.go b/writer/utils/util/string_test.go new file mode 100644 index 00000000..57d807f2 --- /dev/null +++ b/writer/utils/util/string_test.go @@ -0,0 +1,76 @@ +package util + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStringSliceContains(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + inputSlice []string + inputValue string + expected bool + }{ + "should return false on missing value in the slice": { + inputSlice: []string{"one", "two"}, + inputValue: "three", + expected: false, + }, + "should return true on existing value in the slice": { + inputSlice: []string{"one", "two"}, + inputValue: "two", + expected: true, + }, + } + + for testName, testData := range tests { + testData := testData + + t.Run(testName, func(t *testing.T) { + t.Parallel() + + actual := StringSliceContains(testData.inputSlice, testData.inputValue) + assert.Equal(t, testData.expected, actual) + }) + } +} + +func TestStringSnakeCase(t *testing.T) { + t.Parallel() + + cases := []struct { + name string + input, expected string + }{ + { + name: "simple", + input: "snakeCase", + expected: "snake_case", + }, + { + name: "mix", + input: "Snake_Case", + expected: "snake_case", // should be snake__case?? + }, + { + name: "begin-with-underscore", + input: "_Snake_Case", + expected: "_snake_case", + }, + { + name: "end-with-underscore", + input: "Snake_Case_", + expected: "snake_case_", + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got := SnakeCase(c.input) + assert.Equal(t, c.expected, got) + }) + } +} diff --git a/writer/watchdog/watchdog.go b/writer/watchdog/watchdog.go new file mode 100644 index 00000000..8495b34b --- /dev/null +++ b/writer/watchdog/watchdog.go @@ -0,0 +1,50 @@ +package watchdog + +import ( + "fmt" + "github.com/metrico/qryn/writer/service" + "github.com/metrico/qryn/writer/utils/logger" + "github.com/metrico/qryn/writer/utils/stat" + "os" + "time" +) + +var servicesToCheck []service.InsertSvcMap = nil +var lastCheck time.Time + +func Init(services []service.InsertSvcMap) { + servicesToCheck = services + timer := time.NewTicker(time.Second * 5) + go func() { + for _ = range timer.C { + err := Check() + if err != nil { + logger.Error(fmt.Sprintf("[WD001] FATAL ERROR: %v", err)) + os.Exit(1) + } + lastCheck = time.Now() + logger.Info("--- WATCHDOG REPORT: all services are OK ---") + } + }() +} + +func Check() error { + for _, svcs := range servicesToCheck { + for _, svc := range svcs { + _, err := svc.Ping() + return err + } + } + rate := stat.GetRate() + if rate["dial_tcp_lookup_timeout"] > 0 { + return fmt.Errorf("dial_tcp_lookup_timeout happened. System in fatal state") + } + return nil +} + +func FastCheck() error { + if lastCheck.Add(time.Second * 5).After(time.Now()) { + return nil + } + return Check() +}